A short course on Docker

Docker: the layer cache and image size

Two questions everyone who builds Docker images runs into: why does the build take three minutes when I changed one line of code, and why is the image still enormous after I rm-ed all the temporary files. Both have exact answers, and both are measurable with docker build and docker image inspect. The figures here come from Docker 29.7.2 with BuildKit (buildx v0.36.1).

Two lessons, two numbers worth remembering Editing the first RUN line of a six-step Dockerfile makes the five steps after it run again. And an image that writes 20 MB and deletes it in the next RUN measures 25,104,035 bytes — 463 bytes more than the same image that keeps the file.

The two lessons

Twelve to fifteen minutes of reading each, with a lab that runs in the browser.

How the measurements were taken

The rig is a small build directory and a script that runs docker build exactly once per scenario and then reads the CACHED lines from the log. That detail matters: the first version of the script ran the build twice, and the second run always reported every step as CACHED — the resulting table was 100% cache hits and completely meaningless.

DOCKER_BUILDKIT=1 docker build -t dlab:t ctx > /tmp/b.log 2>&1
awk '/^#[0-9]+ \[[0-9]+\/[0-9]+\]/ { … }
     /^#[0-9]+ CACHED/ { … }' /tmp/b.log

docker image inspect dsize:two --format '{{.Size}}'
# 25104035

Image sizes come from docker image inspect --format '{{.Size}}', not from the SIZE column of docker image ls. The two commands disagree: with the containerd image store in use here, docker image ls reports 55.3 MB for the very image that inspect reports as 25,104,035 bytes, because it counts both the compressed blob and the unpacked copy. This course uses only the inspect figure, and always names the command that produced a number.