100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
YAML

Image Layers and Build Caching

How Docker's layered filesystem and build cache work, and how to order Dockerfile instructions to maximize cache hits and minimize image size.

Docker ImagesIntermediate9 min readJul 8, 2026
Analogies

The Union Filesystem and Layers

Each instruction in a Dockerfile that modifies the filesystem (RUN, COPY, ADD) produces a new, immutable layer, identified by a content hash. Docker stacks these layers using a union filesystem (commonly overlay2 on Linux) to present them as a single unified filesystem to the container, while storing them separately and sharing common layers across images.

🏏

Cricket analogy: Each stage of a match report - toss result, innings score, fall of wickets - is recorded as a distinct, unchangeable entry with its own reference number, and the full scorecard stacks these entries together, just as Docker layers stack via a union filesystem while stored separately.

How the Build Cache Works

During docker build, Docker evaluates each instruction in order. For each one, it checks whether a layer already exists in the cache that was produced by the exact same instruction with the same preceding layer chain. If so, it reuses that cached layer instead of re-executing the instruction. As soon as one instruction misses the cache, every subsequent instruction in the Dockerfile also misses, even if it hasn't changed — cache invalidation cascades downward.

🏏

Cricket analogy: When reviewing a rain-affected match, the umpires check if the exact same sequence of overs and conditions occurred before reusing a prior Duckworth-Lewis calculation; once one over's conditions differ, every subsequent recalculation must be redone too, mirroring cascading cache invalidation.

For COPY and ADD, Docker checks the actual checksum of the files being copied, not just the instruction text, to decide on a cache hit. For RUN, Docker compares the literal command string.

Ordering Instructions for Maximum Cache Reuse

Place instructions that change infrequently (installing OS packages, copying dependency manifests, installing dependencies) before instructions that change frequently (copying application source code). This way, editing a single source file only invalidates the cache from that COPY instruction onward, not the expensive dependency-install step above it.

🏏

Cricket analogy: A team sets its base training routine (fitness, fielding drills) early in preseason since it rarely changes, and only finalizes the specific batting order (which changes every match) last, so a single lineup swap doesn't force redoing the whole preseason fitness program.

dockerfile
# Poor ordering — invalidates dependency install on every source change
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

# --- vs ---

# Good ordering — dependency layer only rebuilds when requirements.txt changes
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]

Reducing Layer Count and Size

Every RUN instruction adds a layer, and files deleted in a later layer still consume space in earlier layers because layers are additive diffs, not merges. Chaining related commands into a single RUN with '&&' and cleaning up temporary files within that same instruction avoids leaving stale data behind in the image.

🏏

Cricket analogy: If a groundskeeper spreads extra sand on the pitch during preparation and later 'removes' it by raking over it, the sand is still physically underneath; similarly, files deleted in a later Docker layer still consume space in earlier layers because layers are additive diffs.

dockerfile
# Bad: apt cache remains in an earlier layer even though it's 'removed' later
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*

# Good: single layer, cache cleaned up before the layer is committed
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*

Splitting cleanup into a separate RUN instruction does not shrink the image — the deleted files were already committed to an earlier, immutable layer. Only combining install-and-cleanup into one RUN (or using multi-stage builds) actually removes that weight from the final image.

Inspecting Cache Behavior

During a build, Docker's output indicates 'CACHED' next to steps that reused an existing layer. You can force a full rebuild ignoring the cache with --no-cache, which is useful when debugging suspicious caching issues or ensuring you get fully updated base image content.

🏏

Cricket analogy: During a replay review, the broadcast graphic flags 'ARCHIVE' next to footage reused from an earlier over instead of a fresh camera feed, but the director can force a full fresh re-shoot ignoring all archive footage if something looks suspicious, mirroring 'CACHED' output and '--no-cache'.

bash
docker build -t myapp:1.0 .
# Step 3/7 : COPY requirements.txt .
#  ---> Using cache
#  ---> 4f2e1a9b8c3d
# Step 4/7 : RUN pip install -r requirements.txt
#  ---> Using cache
#  ---> 9a8b7c6d5e4f

# Force a clean rebuild, ignoring all cached layers
docker build --no-cache -t myapp:1.0 .
  • Each RUN, COPY, and ADD instruction produces a new immutable layer.
  • Cache invalidation cascades: once one instruction misses cache, all following instructions rebuild too.
  • Order Dockerfile instructions from least-frequently-changing to most-frequently-changing.
  • Files deleted in a later layer still occupy space in the image because layers store diffs, not merged states.
  • Combine install-and-cleanup steps into a single RUN to actually reduce image size.
  • Use docker build --no-cache to force a full rebuild when cache correctness is in doubt.

Practice what you learned

Was this page helpful?

Topics covered

#YAML#DockerKubernetesStudyNotes#DevOps#ImageLayersAndBuildCaching#Image#Layers#Build#Caching#StudyNotes#SkillVeris