Zoom-in: Docker Container

docker run, app starts immediately. On a local machine, a colleague's machine, or a production server — the same result.
graph LR
D(["🐳 docker run"]) -->|"runs"| C(["📦 Container"])
C -->|"works everywhere"| R(["✅ Result"])
style D fill:#1e293b,stroke:#475569,color:#cbd5e1
style C fill:#1e293b,stroke:#475569,color:#cbd5e1
style R fill:#1e293b,stroke:#475569,color:#cbd5e1
Zoom in on the black box.
Layer 1 — The root problem: "works on my machine"
Before Docker, the environment was something that couldn't be controlled. An app ran on the dev machine because it had the right Python version, the right dependency, the right environment variables. The production server was missing one thing — app crashes.
graph LR
Dev["💻 Dev\nPython 3.11\nlib-v2.3"] -->|"works"| OK["✅ Works"]
Prod["🖥️ Server\nPython 3.9\nlib-v1.8"] -->|"crash"| Fail["❌ ImportError"]
style Dev fill:#1a3a2a,stroke:#22c55e,color:#86efac
style Prod fill:#3b1a1a,stroke:#ef4444,color:#fca5a5
style OK fill:#1a3a2a,stroke:#22c55e,color:#86efac
style Fail fill:#3b1a1a,stroke:#ef4444,color:#fca5a5
The original solution was virtual machines (VMs) — bundle the entire operating system with the app. It worked, but each VM weighs several GB and takes minutes to boot.
Layer 2 — Namespaces: isolation without a VM
Containers aren't a new technology — they use features built into the Linux kernel since 2008: namespaces.
Namespaces partition the kernel into separate "spaces" for each process:
There's no separate operating system — all containers share the host's Linux kernel. This is why containers are so much lighter than VMs.
Layer 3 — cgroups: resource limits
cgroups (control groups) is a Linux kernel feature to limit and monitor resources for a group of processes.
graph TD
Host["🖥️ Host (16GB RAM, 8 CPU)"]
Host --> C1["📦 Container A\n≤ 2GB RAM\n≤ 2 CPU"]
Host --> C2["📦 Container B\n≤ 4GB RAM\n≤ 4 CPU"]
Host --> C3["📦 Container C\n≤ 1GB RAM\n≤ 1 CPU"]
style Host fill:#1a3a2a,stroke:#22c55e,color:#86efac
style C1 fill:#1e3a5f,stroke:#3b82f6,color:#93c5fd
style C2 fill:#1e3a5f,stroke:#3b82f6,color:#93c5fd
style C3 fill:#1e3a5f,stroke:#3b82f6,color:#93c5fd
docker run --memory=2g --cpus=2 translates into cgroup rules for the Linux kernel. When a container exceeds its RAM limit, the kernel's OOM killer terminates the process inside the container — without affecting other containers.
Layer 4 — Union filesystem: image layers
A container image isn't a single file — it's a stack of layers.
Layer 4: App code (5MB) ← changes most often
Layer 3: pip dependencies (120MB)
Layer 2: Python 3.11 (50MB)
Layer 1: Ubuntu base (29MB) ← changes least
When pulling an image, Docker only downloads layers not already present. When building a new image with only app code changes, layers 1–3 are reused from cache — much faster builds.
When a container runs, Docker adds a writable layer on top. All changes inside the container (writing files, creating directories) happen in this layer — the image layers below are unchanged. When the container stops, the writable layer is discarded.
Full picture
From docker run to a process running inside a container.
sequenceDiagram
participant CLI as docker run
participant D as Docker Daemon
participant K as Linux Kernel
CLI->>D: docker run --memory=2g myapp
D->>K: Create PID namespace
D->>K: Create NET namespace + bridge interface
D->>K: Create MNT namespace + union filesystem
D->>K: Create cgroup (≤ 2GB RAM)
D->>K: fork() + exec() process in namespace
K-->>CLI: Container ID abc123
Note over K: Process runs in an isolated "bubble"<br/>but still uses the host's kernel
A container is not a VM — no hypervisor, no guest OS. Just a Linux process with specially configured namespaces and cgroups.
Three common misconceptions
Containers and VMs are the same, just different names
VMs run their own OS — fully isolated, minutes to boot, several GB each. Containers share the host kernel — lighter, faster, but less isolated. A process escaping a container namespace (container escape) can affect the host — not possible with a VM.
Docker images are completely immutable
The image layers below are immutable, but the writable layer while the container is running is not. Data written inside a container is lost when the container stops — use a volume or bind mount to persist data. Many people lose data on container restart because of this misunderstanding.
Containers are secure by default
Containers run as root inside by default unless configured otherwise. If a container is compromised, an attacker has root inside the container — and with a volume mount or privileged mode, can escalate to the host. Always run containers with a non-root user in production.
Takeaway
Containers exist because the Linux kernel already had the tools to create isolated environments without VMs: namespaces for isolation, cgroups for resource limits, union filesystem for reusable image layers. Docker is just a friendly interface that packages all three together.
When debugging a container, the right question is: "is the problem in the namespace, the cgroup, or the filesystem layer?" — docker inspect, docker stats, and docker exec show the state of each layer.
Article assisted by Amy 🌸 - AI Assistant. Content reviewed by the author.
Related Posts
Docker v29 Breaks Backward Compatibility: 3 Major Changes and How to Migrate Safely
Docker Engine v29 makes containerd image store the default, raises minimum API version to 1.44, and adds nftables support. Here's what developers and DevOps need to know.
Docker vs Podman vs containerd 2026: Which Container Runtime for Production?
Docker still dominates, but Podman 5 rootless and containerd 2.0 are reshaping the landscape. A practical comparison of security, performance, and Kubernetes compatibility.
Zoom-in: TCP
Every HTTP request runs on TCP — but before the first byte of real data crosses the wire, three packets are exchanged carrying no data at all. TCP solves the problem the Internet doesn't.