Software has a long history of "it works on my machine" failures: an app runs fine on the developer's laptop, but breaks in staging, breaks again in production, and breaks differently on every customer's server. Containers are the technology that solved this — by packaging an application together with everything it needs to run, into a single portable unit.
The Core Idea
A container bundles four things together:
- The application code or binary
- Its runtime (Python, Node, JVM, .NET, etc.)
- System libraries and tools it depends on
- Configuration files and environment variables
The result is an artefact that runs identically on any machine with a container runtime — your laptop, a CI server, AWS, GCP, Azure, your colleague's MacBook, a Raspberry Pi. The bundle is described by an image; a container is a running instance of that image.
Containers vs Virtual Machines
Both VMs and containers provide isolation, but at different layers:
| Virtual Machine | Container | |
|---|---|---|
| Includes | Full guest OS + kernel + app | App + libraries (shares host kernel) |
| Size | Gigabytes | Megabytes to ~1 GB |
| Boot time | Seconds to minutes | Milliseconds to seconds |
| Isolation | Strong (hypervisor) | Weaker (process-level via kernel features) |
| Density per host | Tens | Hundreds to thousands |
| Use case | Mixed OS workloads, untrusted multi-tenant | Microservices, CI/CD, scaling stateless apps |
Containers are not VMs. They share the host's Linux kernel. This makes them lightweight but means a Linux container only runs on a Linux kernel. (Docker Desktop on Mac/Windows uses a small Linux VM behind the scenes to provide that kernel.)
How Containers Work Under the Hood
Containers are built from Linux kernel features that have existed for years:
- Namespaces isolate what a process can see — its own process IDs, network interfaces, hostname, mount points, and users.
- Control groups (cgroups) limit what a process can use — CPU, memory, disk I/O, network bandwidth.
- Union filesystems (overlayfs, aufs) layer image content efficiently so common base layers can be shared between containers.
Docker's innovation in 2013 was not inventing these primitives — it was wrapping them in a simple developer experience: docker run nginx, and you have a web server.
Why Containers Took Over
- Reproducibility: The image is the contract. If it runs in dev, it runs in prod.
- Density: A single VM can host hundreds of containers, dramatically lowering cloud bills.
- Speed: Spin up in milliseconds instead of minutes — perfect for CI, autoscaling, and serverless.
- Microservices: Containers are the natural deployment unit when you split a monolith into many small services.
- Cloud portability: An OCI-compliant image runs on any cloud's container service.
The OCI Standards
To prevent vendor lock-in, the Open Container Initiative (OCI), formed in 2015, standardised:
- Image specification — how images are structured (manifest, config, layers).
- Runtime specification — how containers are launched.
- Distribution specification — how registries serve images.
This means an image you build with Docker can be run by alternative runtimes (containerd, CRI-O, Podman) and stored in any OCI-compliant registry (Docker Hub, GitHub Container Registry, Amazon ECR, Azure ACR, Google Artifact Registry).
The Ecosystem
| Tool | Role |
|---|---|
| Docker | The most popular tool to build, run, and ship containers locally |
| containerd | The lightweight runtime that Docker (and Kubernetes) use under the hood |
| Podman | Rootless, daemonless Docker alternative — popular on Red Hat systems |
| Kubernetes | Orchestrator that schedules and manages containers across many machines |
| Docker Compose | Define and run multi-container apps with a single YAML file |
The next lesson installs Docker and walks through its architecture so you can run your first container.