← Learn
Docker Course  /  Phase 1  /  Module 00
Day 0 ~1h · mental model
Phase 1 · Docker · Day 0

Module 00 · Foundations
What a Container Actually Is

Meet Sam, a developer about to ship their first containerized app. If Sam only memorises commands, Sam will fall down on the why, the very thing that separates juniors from professionals. This module builds the mental model first: what problem containers solve, what actually makes one isolated, and how Docker's pieces fit together. No fluff.

~1h · conceptual mental model first the "why", not just the "how" Docker · OCI · Linux
1

The problem containers solve

Sam's app runs perfectly on Sam's laptop. Then a teammate pulls it and it dies on import. "It works on my machine." Classic. The app needs a specific Python version, certain system libraries, env vars, a particular Linux distro. Move it to another machine and any mismatch breaks it.

Two historical fixes came before containers:

Install directly on the server

Fragile and unrepeatable. Apps conflict (App A needs OpenSSL 1.1, App B needs 3.0, and they share one host).

Virtual Machines

Each app gets a full guest OS. Strong isolation, but heavy: gigabytes of RAM/disk per VM, slow to boot.

The container idea

Containers are the third fix, and the one that ends Sam's "works on my machine" headache: package the app and its dependencies into one portable artifact (an image), and run it as an isolated process on a shared kernel. Lightweight like a process, portable like a VM.

2

Containers vs Virtual Machines

This comparison is the bedrock. A VM stack carries a full guest OS per app; a container stack shares one host kernel and stacks only the app and its deps on top.

the two stacks, side by side
   VIRTUAL MACHINES                      CONTAINERS
 ┌──────┐ ┌──────┐ ┌──────┐           ┌──────┐ ┌──────┐ ┌──────┐
 │ App  │ │ App  │ │ App  │           │ App  │ │ App  │ │ App  │
 │ Bins │ │ Bins │ │ Bins │           │ Bins │ │ Bins │ │ Bins │
 │Guest │ │Guest │ │Guest │           └──────┘ └──────┘ └──────┘
 │  OS  │ │  OS  │ │  OS  │           ┌──────────────────────────┐
 └──────┘ └──────┘ └──────┘           │   Container Engine       │
 ┌──────────────────────────┐         ├──────────────────────────┤
 │       Hypervisor          │         │      Host OS (kernel)    │
 ├──────────────────────────┤         ├──────────────────────────┤
 │       Host OS             │         │       Hardware           │
 ├──────────────────────────┤         └──────────────────────────┘
 │       Hardware            │
 └──────────────────────────┘
Virtual MachineContainer
Isolation unitFull guest OS + virtual hardwareIsolated process on the host kernel
KernelIts ownShares the host kernel
SizeGBsMBs
StartupSeconds–minutesMilliseconds–seconds
OverheadHigh (full OS per VM)Low (just the app + deps)
Isolation strengthStronger (hardware-virtualized)Weaker (kernel-level), relevant to security
Use caseRun different OSes, strong tenant isolationPackage/ship/scale apps, microservices
The single sentence to remember

A VM virtualizes hardware and runs a full OS; a container virtualizes the operating system and shares the host kernel.

The consequence that trips people up

Because containers share the host kernel, a Linux container needs a Linux kernel. On a Linux machine it runs natively. On macOS/Windows, Docker runs a lightweight Linux VM under the hood and your containers run inside that. So "containers don't need a VM" is only true on Linux.

3

What actually makes a container isolated

Sam pictured a container as some magic box. It isn't. A container is not a special object. It's a normal Linux process that the kernel has been told to restrict and isolate using three kernel features. This is the heart of the foundations.

Namespaces

What the process can SEE. They partition kernel resources so a process gets its own isolated view.

cgroups

What the process can USE. They limit and account for CPU, memory, I/O, and process count.

Union/overlay FS

How image layers become a single writable root via copy-on-write.

a) Namespaces: "what the process can SEE"

Namespaces partition kernel resources so a process sees its own isolated view. The key ones:

NamespaceIsolates
PIDIts own process tree. The main process is PID 1; it cannot see host processes.
NETIts own network interfaces, IP, routing table, ports.
MNTIts own filesystem mount points (its own root /).
UTSIts own hostname.
IPCIts own inter-process communication (shared memory, semaphores).
USERMaps user IDs; lets a process be "root" inside the container but unprivileged on the host (the basis of rootless/hardening, Module 07).
Mnemonic

MNT, UTS, IPC, NET, PID, USER → "Make Unix Isolation Now, Please User."

b) cgroups (control groups): "what the process can USE"

cgroups limit and account resource usage: CPU, memory, disk I/O, network bandwidth, number of processes (PIDs). This is how docker run --memory=256m works, and it maps directly to Kubernetes resources.requests/limits later.

The one-liner that answers a common question

Namespaces isolate (visibility); cgroups limit (consumption).

c) Union/overlay filesystem: "how the image becomes a writable root"

An image is a stack of read-only layers. When you start a container, the engine adds a thin writable layer on top (copy-on-write). Your storage driver overlayfs merges them into a single view. Delete the container and the writable layer is gone; the underlying image layers are untouched and reused by other containers. (Layers and caching = Module 02; storage persistence = Module 03.)

⬆ Writable container layerper-container · gone on rm
RO layer · app codeimage
RO layer · pip/npm depsimage · shared
RO layer · base OS (e.g. debian)image · shared

overlayfs merges read-only image layers + one writable layer into a single root /.

Container, defined precisely

A host process, isolated by namespaces, resource-bounded by cgroups, running on a copy-on-write union filesystem assembled from image layers.

4

Image vs container: do not confuse these

Image = blueprint

The read-only template. Built from a Dockerfile. Immutable. Stored as layers.

Container = instance

A running (or stopped) instance of an image, with its own writable layer and isolated namespaces.

Analogy

Image is the class, container is the object. Or: image is the recipe, container is the cooked dish. Sam can run many containers from one image, which is exactly what happens when the app scales out.

5

Docker's architecture: client / daemon / registry

When Sam types docker run, several pieces move at once. Docker is not one program. Knowing the split (and being able to narrate it) is what signals you understand the stack.

the request path
  docker CLI  ──REST API──►  dockerd (daemon)  ──pull/push──►  Registry (e.g. Docker Hub)
  (client)                   │  builds images
                             │  runs containers (via containerd → runc)
                             ▼
                        Linux kernel (namespaces, cgroups)
ComponentRole
Docker client dockerWhat you type. Sends commands over a REST API (local socket /var/run/docker.sock by default).
Docker daemon dockerdThe long-running server. Builds images, runs/manages containers, networks, volumes. Client and daemon can be on different machines.
containerdHigh-level container runtime the daemon delegates to (image transfer, container lifecycle). Also what Kubernetes uses directly.
runcThe low-level runtime that talks to the kernel to create the namespaces/cgroups and spawn the process. OCI-compliant.
RegistryStores and distributes images (Docker Hub, GHCR, ECR, a private registry, covered in Module 06).
What happens on "docker run nginx"

Trace it: client → daemon → check local image, else pull from registry → containerd → runc → create namespaces/cgroups → start process. Being able to narrate that signals you understand the whole stack, not just the CLI.

OCI: the standards

OCI (Open Container Initiative) defines open specs for the image format and the runtime. This is why images are portable across Docker, Podman, Kubernetes, and more. "Docker image" is colloquial; the precise term is "OCI image." Knowing this signals maturity.

6

Where this is heading

Every concept here reappears later. As Sam moves toward Kubernetes, keep asking "how does this reappear there?" That habit is the senior mindset.

Foundation conceptWhere it reappears
cgroups limitsKubernetes requests/limits and the scheduler.
NET namespace + bridgeKubernetes pod networking and CNI plugins.
daemon / containerd / runc splitWhy Kubernetes dropped the Docker shim and talks to containerd directly.
Declarative image buildDeclarative deployment manifests.
7

Hands-on: prove isolation yourself

Sam doesn't take any of this on faith, and neither should you. The point of this exercise is to see namespaces and cgroups with your own eyes. Run each line and predict what it will show before you hit enter.

exercise: prove a container is just an isolated process
# 1. Start a long-lived container in the background
docker run -d --name demo nginx:1.27

# 2. PID namespace: inside, nginx is PID 1 and sees no host processes
docker exec demo sh -c 'ps -ef'
PID  CMD
  1  nginx: master process ...   <- PID 1 inside the container

# 3. Same process, seen from the HOST, a normal process with a big PID
ps -ef | grep nginx          # it's just a host process, not PID 1 here

# 4. UTS namespace: the container has its own hostname
docker exec demo hostname

# 5. cgroups: cap memory and watch the limit be enforced
docker run --rm --memory=256m alpine sh -c 'cat /sys/fs/cgroup/memory.max'

# 6. Clean up
docker rm -f demo
  • You saw the container's main process as PID 1 inside, but a normal host process from outside.
  • You confirmed the container has its own hostname (UTS namespace).
  • You saw a memory limit enforced by cgroups.
  • You can recite the precise definition: a host process, isolated by namespaces, bounded by cgroups, on a copy-on-write union filesystem.
Self-check before moving on

Can you cleanly answer: VM vs container? Namespaces vs cgroups? Image vs container? What happens on docker run nginx? If any of those feel shaky, re-read sections 2, 3, and 5 before Module 01.