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:

$ 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.