Images are stacks of layers
How images are stored sounds like trivia, and it decides two things you feel every day: how long docker build takes, covered next lesson, and how long every push, pull, and deploy takes, covered in unit 6.
An image is not one big blob. Each instruction in the Dockerfile from lesson 3-1 produces a layer, a read-only diff containing only what that instruction changed.
| Instruction | Layer contains |
|---|---|
FROM python:3.12-slim | the base layers, OS files and Python |
COPY requirements.txt . | one small file |
RUN pip install ... | the installed packages |
COPY . . | your source code |
The final image is this stack read together. When a container runs, Docker puts the thin writable layer from lesson 1-2 on top, and everything below stays read-only forever.
The payoff is sharing. If ten of your images all start FROM python:3.12-slim, those base layers exist once on disk and are downloaded once, and only each image's small unique layers differ.
Seeing the layers with docker history
The stack is not hidden, and one command prints it:
docker history myapp:v1
IMAGE CREATED BY SIZE 8f3c2a91d0e4 CMD ["python" "server.py"] 0B 2b1a99c47f21 COPY . . 1.2MB 91c04d55ab02 RUN pip install -r requirements.txt 89MB 77e5301bfa6c COPY requirements.txt . 412B ... FROM python:3.12-slim 150MB
One row per instruction, newest on top, with the size each layer added.
Engineers reach for this when an image is mysteriously huge, because the SIZE column points straight at the guilty instruction. Here the pip install layer costs 89 MB, which is normal, while a 900 MB COPY . . row would mean junk files are being copied in.
Note also that CMD costs 0 B, which confirms it is metadata rather than content. The .dockerignore file in the next lesson is the fix for a bloated copy layer.
How often a shared base layer is stored
Ten images that all start FROM python:3.12-slim store those base layers once.
Layers are stored and downloaded once, then shared by every image and container that references them. Docker identifies each layer by a hash of its content, so an identical layer is recognized as the same object no matter which image pulled it in.
| Setup | Disk cost |
|---|---|
| ten full copies | ten times the base |
| ten images sharing a base | one base plus ten thin app layers |
This sharing is the reason images are practical at all. It is also why choosing the same base image across a fleet of services is a real optimization rather than a style preference.
Diagnosing an 850 MB copy layer
When docker history shows an 850 MB COPY . . layer against 2 MB of source code, the build context almost certainly contained huge extra files that the copy pulled in.
A layer contains exactly what its instruction added, so an 850 MB copy layer means 850 MB of files were copied from the build context.
| Usual culprit | Typical size |
|---|---|
.git history | tens to hundreds of MB |
node_modules or a virtualenv | hundreds of MB |
| data files and logs | anything |
The layer size is trustworthy evidence, which makes this an unusually easy diagnosis. The .dockerignore file in the next lesson excludes those paths from the context so the copy never sees them.
A layer. Every instruction produces one, the image is the ordered stack of them, and they are shared across images to save disk and download time. Layers also power the build cache, which is the next lesson.