Images Are Onions
Layers
An image isn't one big file - it's a stack of read-only layers, each recording the filesystem changes of one build step:
┌─────────────────────────────┐ ← your app code (layer 4, 2 MB)
│ COPY app/ /app │
├─────────────────────────────┤ ← dependencies (layer 3, 60 MB)
│ RUN pip install -r reqs.txt │
├─────────────────────────────┤ ← runtime (layer 2, 25 MB)
│ RUN apk add python3 │
├─────────────────────────────┤ ← base OS (layer 1, 7 MB)
│ FROM alpine:3.20 │
└─────────────────────────────┘
Why this design is brilliant:
- Sharing - 50 containers from one image share the same layers on disk; each container adds only a thin writable layer
- Caching - rebuild only the layers whose inputs changed (change app code → only layer 4 rebuilds)
- Fast pulls -
docker pullskips layers you already have
$ docker image history nginx:alpine # see every layer + its size
$ docker image inspect -f '{{.Size}}' nginx:alpine
Analogy: Layers are like Git commits for filesystems: each records a diff on top of its parent, identical history is shared, and identical layers are stored exactly once.