Docker Tutorial — What Are Containers?
A container is a lightweight, isolated environment that packages your application code together with all its dependencies — libraries, runtime, system tools — so it runs the same way everywhere. Think of it as a shipping container for software: standardized, portable, and self-contained.
The Problem: “Works on My Machine”
Every developer has heard (or said) this phrase. The app works perfectly on your laptop, but crashes in staging. Why?
- Your laptop has Python 3.11, staging has 3.9
- You installed
libssl-devmonths ago, the server never got it - Your local config points to
localhost, production expects a different hostname - Different OS versions handle file paths differently
The root cause: environments differ. Your code doesn’t run in isolation — it depends on dozens of system-level components that vary between machines.
What Is a Container?
A container is a process running on your machine — but with isolation. It has:
- Its own filesystem — sees only the files packaged inside it
- Its own network — gets its own IP address, ports, DNS
- Its own process tree — PID 1 inside the container is just a regular process to the host
- Resource limits — can be capped on CPU, memory, I/O
+----------------------------------+
| Your Application |
| (code, configs, binaries) |
+----------------------------------+
| Runtime & Libraries |
| (Python 3.11, Node 20, etc.) |
+----------------------------------+
| Base OS Libraries |
| (libc, libssl, ca-certs) |
+----------------------------------+
| Container Runtime Layer |
+----------------------------------+
| Host OS Kernel |
+----------------------------------+
The key insight: containers share the host kernel but isolate everything else. This makes them much lighter than virtual machines.
How Containers Work: Linux Kernel Features
Containers aren’t magic — they use two Linux kernel features:
Namespaces (Isolation)
Namespaces give each container its own isolated view of system resources:
| Namespace | What It Isolates |
|---|---|
| PID | Process IDs — container sees its own PID 1 |
| NET | Network interfaces, IP addresses, ports |
| MNT | Filesystem mount points |
| UTS | Hostname |
| IPC | Inter-process communication |
| USER | User and group IDs |
cgroups (Resource Limits)
Control groups limit how much resource a container can use:
| Resource | What It Controls |
|---|---|
| CPU | How much CPU time the container gets |
| Memory | Maximum RAM the container can use |
| I/O | Disk read/write bandwidth |
| Network | Network bandwidth limits |
The Shipping Container Analogy
Before standardized shipping containers (invented in the 1950s), loading cargo onto ships was chaotic. Every crate was a different size. Dockers had to carefully arrange each piece.
Standardized containers changed everything:
- Any cargo fits inside the same container shape
- Any ship, truck, or train can carry the same container
- Loading/unloading became automated and fast
Software containers work the same way:
- Any application (Python, Node, Java, Go) fits inside a container
- Any machine (laptop, server, cloud VM) can run the same container
- Deployment becomes automated and predictable
A Brief History of Containers
| Year | Milestone |
|---|---|
| 1979 | chroot — first form of filesystem isolation on Unix |
| 2000 | FreeBSD Jails — process isolation beyond just filesystem |
| 2004 | Solaris Zones — full OS-level virtualization |
| 2006 | cgroups added to Linux kernel (by Google engineers) |
| 2008 | LXC (Linux Containers) — first complete container solution on Linux |
| 2013 | Docker launched — made containers accessible to everyone |
| 2015 | OCI founded — industry standards for container images and runtimes |
| 2017 | containerd donated to CNCF — Docker’s runtime becomes an industry standard |
| 2020+ | Containers become the default deployment model for cloud-native apps |
Docker didn’t invent containers. It made them simple to use.
Containers Are Just Processes
This is the most important mental model: a container is a regular Linux process with extra isolation.
# Run an nginx container
$ docker run -d --name web nginx
# On the HOST, find the container's process
$ ps aux | grep nginx
root 12345 0.0 0.1 ... nginx: master process nginx -g daemon off;
# Inside the container, it thinks it's PID 1
$ docker exec web ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT COMMAND
root 1 0.0 0.1 ... nginx: master process nginx -g daemon off;
Same process. Two different views. That’s namespaces at work.
Summary
| Concept | Key Takeaway |
|---|---|
| Container | Isolated process with its own filesystem, network, and process tree |
| Namespaces | Linux kernel feature providing isolation (PID, NET, MNT, etc.) |
| cgroups | Linux kernel feature limiting resources (CPU, memory, I/O) |
| Shared kernel | Containers share the host OS kernel (unlike VMs) |
| Portability | Same container runs identically on any machine with a container runtime |
| History | chroot (1979) → LXC (2008) → Docker (2013) → OCI standards (2015) |
Quick Reference
Container = Process + Isolation (namespaces) + Resource Limits (cgroups)
Key Linux features:
- PID namespace → isolated process tree
- NET namespace → isolated network stack
- MNT namespace → isolated filesystem
- cgroups → CPU, memory, I/O limits
Container ≠ VM:
- Containers share the host kernel
- VMs run their own kernel on a hypervisor