Before learning Docker, you must understand why it exists and the problem it solves. The history of application hosting tells a clear story of each generation solving the failures of the previous one.
Era 1: The Bare Metal Era (1990s)
Deploying an application meant buying a physical server β installing Linux, configuring networking, installing runtime dependencies (Java, Node, Python), and copying your code.
The fundamental problem: Two applications on the same server often conflict. App A needs Python 2, App B needs Python 3. App A needs OpenSSL 1.0, App B needs OpenSSL 3. Solving this meant running one app per physical server β an incredibly wasteful use of hardware that typically sat at 5β15% CPU utilisation.
Era 2: Virtual Machines (2000s)
Hypervisors (VMware, VirtualBox, KVM, Hyper-V) solved the "one app per server" waste problem by splitting one physical machine into multiple isolated Virtual Machines.
How it works: The hypervisor emulates hardware. Each VM sees what it believes is a real physical machine β with virtual CPU, virtual RAM, virtual disk. Each VM runs its own full Guest OS on top.
What VMs solved: Multi-tenancy on one physical machine, strong isolation between workloads, different OSes side-by-side.
What VMs did NOT solve:
- Startup time: A VM takes 30β120 seconds to boot (it's loading a full OS)
- Memory waste: A 10 MB Node.js app carrying a 2.5 GB Ubuntu installation is absurd
- Density: You can fit ~10 VMs on a server before running out of RAM just from Guest OS overhead
- Portability: Moving a VM between hypervisors is painful; moving between cloud providers is nearly impossible
Era 3: Containers (2013βPresent)
Docker (launched 2013) popularized a fundamentally different approach: instead of virtualizing the hardware, containers virtualize the Operating System.
The key insight: All containers on a host share the same underlying Linux kernel. They don't need their own OS β just their own isolated slice of the existing one.
Linux provides two kernel features that make this possible:
- Namespaces: Isolate what a process can see β its own PID namespace (so it can't see other processes), network namespace (its own network stack), mount namespace (its own filesystem view), user namespace (its own UIDs)
- cgroups (Control Groups): Limit what a process can use β CPU time, memory, I/O bandwidth
Side-by-Side Comparison
| Bare Metal | Virtual Machine | Container | |
|---|---|---|---|
| Isolation | None | Full hardware emulation | OS-level namespaces |
| Startup time | Minutes (manual) | 30β120 seconds | < 1 second |
| Image size | N/A | 1β10 GB | 5β500 MB |
| Memory overhead | None | 1β2 GB per Guest OS | Near zero |
| Density | 1 app/server | 5β20 VMs/server | 50β500 containers/server |
| Portability | None | Limited (hypervisor-specific) | Runs identically anywhere |
| Startup speed | Weeks (hardware procurement) | Minutes | Milliseconds |
The "Works On My Machine" Problem β Solved
This is Docker's most famous value proposition. Before containers, this conversation happened constantly:
A Docker container includes not just your code, but the exact runtime (Node 18.x), the exact libraries (libssl 3.0), the exact configuration files β everything the app needs to run. The container is the "machine". If it works in a container on your laptop, it works in the same container on the CI server, on the staging server, and on the production server. The environment travels with the code.
Interactive Terminal Sandbox: Docker Runtime
Explore running container processes and images directly in the browser:
Practice in 1-Click Free Cloud Sandboxes
Don't have Linux or Docker installed locally? You can run every exercise in this curriculum using these free, instant browser-based sandboxes:
Killercoda Interactive Playground
Free real Ubuntu & Kubernetes Linux instances in browser without signup.
Play with Docker (PWD)
Free 4-hour Docker lab instances with full root daemon access.
GitHub Codespaces
60 hours/month free cloud VS Code and Linux container environment.
Knowledge Check
What allows 100+ Docker containers to run simultaneously on a single modest Linux server?
Summary
- Bare Metal: one app per physical server β expensive and wasteful
- Virtual Machines: strong isolation + hardware emulation β heavy (GB-sized Guest OS per VM, minute-long boots)
- Containers: OS-level isolation via Linux namespaces + cgroups β lightweight (MB-sized, millisecond boots), portable, dense
- The container image bundles the app + its exact dependencies β eliminating "works on my machine" permanently
- Containers share the host kernel β faster and smaller but thinner isolation boundary than VMs
In the next lesson, you will learn how Docker is architectured internally β the client, daemon, registry, and the difference between images and containers.