Frontend Labs
⋯

Chapter 46 — Images and layers: why containers start in milliseconds

In one sentence

A container image is built from a stack of read-only layers, each one a set of filesystem changes, combined at runtime by a union filesystem — and because unchanged layers can be cached and reused across many containers, starting a new container almost never means copying a whole filesystem from scratch.

Why this matters

Understanding layers explains why docker build caching works the way it does, why images can be surprisingly large or surprisingly small, and why docker pull only downloads what's actually new.

How it actually works

   AN IMAGE is a STACK of LAYERS, each one representing a set of
   filesystem CHANGES (files added, modified, or removed) relative to
   the layer below it. Every instruction in a Dockerfile that changes
   the filesystem (`RUN`, `COPY`, `ADD`) typically creates a NEW layer:

     FROM ubuntu:22.04          <- base layers (the OS itself)
     RUN apt-get install -y curl   <- a new layer: curl's files added
     COPY app.py /app/               <- a new layer: your one file added
     CMD ["python3", "/app/app.py"]  <- metadata only, no filesystem
                                     change, no new layer

   THESE LAYERS ARE READ-ONLY and, crucially, CACHEABLE and SHAREABLE:
   if two different images both start `FROM ubuntu:22.04`, they can
   literally SHARE that base layer on disk -- it's stored ONCE, not
   duplicated. When you `docker pull` an image, Docker only downloads
   layers it doesn't ALREADY have locally.

   A UNION FILESYSTEM (commonly OverlayFS on modern Linux) is what
   makes running a container from these layers actually WORK: it
   presents the STACK of read-only layers as if they were a SINGLE,
   unified filesystem, and adds ONE thin, WRITABLE layer on top,
   specific to THIS running container -- any file the container creates
   or modifies while running goes into THAT layer, leaving the
   underlying image layers completely untouched.

   THIS IS EXACTLY WHY CONTAINER STARTUP IS SO FAST: starting a new
   container from an image ALREADY ON DISK doesn't require copying
   gigabytes of filesystem -- it just needs to set up this thin
   writable layer on top of ALREADY-PRESENT, READ-ONLY layers, then
   start the process (Chapter 44). It's a direct, practical consequence
   of Chapter 15's "renaming/linking is cheap, copying data is
   expensive" lesson, applied at the whole-filesystem scale.

Real-world example

   "Why did changing ONE LINE in my Dockerfile trigger a rebuild of
   EVERYTHING" is a genuinely common, well-documented real question new
   Docker users ask, and it's a direct, practical consequence of THIS
   chapter's layer-caching mechanism: Docker's build cache works, by
   design, LAYER BY LAYER, IN ORDER -- if you change (or invalidate) a
   layer, EVERY LAYER AFTER IT in the Dockerfile must be rebuilt too,
   even if those later instructions themselves didn't change at all,
   because each layer is built ON TOP of the previous one's exact
   output.
   This is EXACTLY why Docker's own official best-practices
   documentation, and essentially every serious Dockerfile style guide,
   recommends ORDERING instructions from LEAST-likely-to-change to
   MOST-likely-to-change: install your OS packages and dependencies
   FIRST (these change rarely), and COPY your actual, frequently-
   changing application code LAST. A Dockerfile that does the reverse --
   copying application code early, then installing dependencies -- means
   EVERY single code change (which might happen dozens of times a day
   during active development) invalidates the dependency-installation
   layer too, needlessly re-running a slow `apt-get`/`pip install`/`npm
   install` step on every single build, turning what should be a
   near-instant rebuild into a genuinely painful, minutes-long one. This
   real, common performance mistake -- and its well-documented fix -- is
   entirely explained by understanding layers as this chapter describes
   them, rather than treating Dockerfile ordering as arbitrary.

Try it yourself

# See an image's actual layers
docker pull alpine
docker history alpine

# Build something yourself and watch layer caching in action
mkdir -p ~/lab/dockerdemo && cd ~/lab/dockerdemo
cat > Dockerfile << 'EOF'
FROM alpine:latest
RUN echo "installing dependencies (pretend)" && sleep 2
COPY app.txt /app.txt
CMD ["cat", "/app.txt"]
EOF
echo "version 1" > app.txt

docker build -t layerdemo .              # full build, note the timing
echo "version 2" > app.txt
docker build -t layerdemo .              # rebuild -- notice the FIRST
                                         # layer (the slow RUN/sleep)
                                         # is CACHED, only the COPY
                                         # layer (and after) rebuilds

# See the difference an image makes on disk
docker images | grep -E "alpine|layerdemo"

Common mistakes

  • Putting frequently-changing files (application code) early in a Dockerfile. Invalidates every subsequent layer's cache on every change — order from least-changing to most-changing instead.
  • Assuming a container's writable layer changes are permanent. By default, they're LOST when the container is removed — Chapter 49's volumes are the mechanism for data that must actually persist.
  • Not realising shared base layers save real disk space and pull time. Standardising on a small number of base images across your organisation is a genuine, practical optimisation, not just tidiness.

Check yourself

  1. What is a layer, and why are layers cacheable/shareable across images?
  2. What does the writable layer contain, and what happens to it when a container is removed?
  3. Why should a Dockerfile install dependencies BEFORE copying application code, in terms of build-cache behaviour?

Further reading

  • Docs: Docker's own official "Best practices for writing Dockerfiles" — the layer-ordering guidance directly, from the source.
  • Reference: man 8 mount (the overlay filesystem type), or search "OverlayFS explained" for a clear technical breakdown.
  • Practice: docker history <image> on a few real, popular public images — a genuinely instructive way to see real-world layer structure.

Generated from real-life-os-guide.md · 8 Oct 2026 16:36