When engineers struggle with Docker, it is almost never because the syntax is difficult. It is because their mental model treats containers like lightweight virtual machines.
A container is not a machine. A container is a regular Linux process running with kernel-enforced namespace isolation and cgroup resource boundaries, rooted in a stacked read-only filesystem with a thin copy-on-write layer on top.
Once this model clicks, commands like docker commit, volume mounts, and multi-stage builds become completely intuitive.
1. The Stacked Filesystem: Images as Immutable Deltas
A Docker image is fundamentally a collection of read-only tarballs stacked on top of one another via an overlay filesystem (such as overlay2).
Every instruction in your Dockerfile creates a layer:
# Layer 1: Base distribution binaries and directories
FROM alpine:3.20
# Layer 2: Package manager updates and runtime
RUN apk add --no-cache nodejs npm
# Layer 3: Application source code
WORKDIR /app
COPY package.json ./
RUN npm install --production
COPY . .
# Metadata only (no disk layer created)
EXPOSE 3000
CMD ["node", "server.js"]Each layer only records the diff between itself and the layer below it. If Layer 2 creates a file and Layer 3 deletes it, Layer 3 does not actually remove bytes from Layer 2. Instead, Layer 3 writes a special marker called a whiteout file that hides the underlying file from the unified view.
Layer Properties Comparison
| Property | Image Layer | Container Layer | Volume |
|---|---|---|---|
| Mutability | Read-only (Immutable) | Read-write (Ephemeral) | Read-write (Persistent) |
| Lifecycle | Persists across containers | Destroyed with container | Decoupled from container |
| Storage Driver | overlay2 (host disk) | overlay2 (Copy-on-Write) | Direct host mount / block device |
| Performance | High read speed | Write penalty on first write | Native filesystem throughput |
2. Containers: Isolated Processes with a Scratchpad
When you run docker run -d my-app, Docker does not boot an operating system kernel. Instead, it performs two distinct actions:
- Allocates a writable layer: Places a thin, writable container layer directly atop the immutable image stack.
- Spawns the process: Forks a new process under
clone()with namespaces forPID,NET,MNT,IPC,UTS, andUSER.
# Inspect the mount layers of any active container
docker inspect --format '{{json .GraphDriver.Data}}' my-running-containerThe Copy-on-Write (CoW) Penalty
When a process inside a container modifies an existing file that came from an image layer:
- The kernel searches the overlay stack downwards to find the file.
- The entire file is copied up into the container's writable scratchpad layer.
- The process modifies the newly copied file in place.
Key Takeaway: If your application frequently writes to disk (such as databases, log files, or temp files), never write directly to the container layer. The copy-on-write operation introduces measurable I/O latency and bloats your container scratch space.
3. Volumes: Piercing the Abstraction
Docker volumes bypass the overlay2 storage driver completely.
When you attach a volume:
docker run -d \
--name postgres-db \
-v pgdata:/var/lib/postgresql/data \
postgres:16-alpineThe directory /var/lib/postgresql/data inside the container is directly mounted to a host path (typically under /var/lib/docker/volumes/pgdata/_data).
Writes to this directory bypass the container layer entirely. They hit the host filesystem at native speed, and they survive container removal (docker rm -f postgres-db).
Summary
- Image: A cryptographic hash chain of read-only tarball deltas.
- Container: An isolated Linux process with a temporary, writable scratchpad layer mounted on top.
- Volume: A direct host mount that bypasses the copy-on-write driver for native I/O performance and persistence.