Stage 4 · Provision
Building Images
Layers & Build Cache
Order Dockerfile instructions for cache reuse, understand layer diffs, and diagnose cache misses.
How Layers Work
Every instruction in a Dockerfile creates a layer. A layer is a set of filesystem changes: added files, modified files, deleted files, and metadata. Layers are immutable and stacked. When you change one instruction, only the layers after it (and the changed layer itself) need to rebuild.
FROM node:20-alpine # Layer 1: base image
WORKDIR /app # Layer 2: directory created
COPY package*.json ./ # Layer 3: package files copied
RUN npm ci --production # Layer 4: dependencies installed
COPY . . # Layer 5: source code copied
CMD ["node", "server.js"] # Layer 6: metadata only (no size)Each instruction creates exactly one layer. The base image layers are pulled from the registry. Your instructions add layers on top. CMD and ENV create metadata layers with zero filesystem changes.
Build Cache
Docker caches every layer it builds. On subsequent builds, Docker checks each instruction against the cache. If the instruction and its inputs have not changed, Docker reuses the cached layer instead of rebuilding. This makes iterative development fast.
# First build — everything builds from scratch
docker build -t myapp .
# Second build — uses cache for unchanged layers
docker build -t myapp .
# Change a source file — only COPY . . and layers after it rebuild
# Everything before it uses cache
docker build -t myapp .If you modify app.js, only the COPY . . layer and anything after it rebuild. The npm ci layer is cached because package*.json did not change.
Cache Invalidation
A layer is invalidated when its input changes. For COPY, this means any file change. For RUN, Docker cannot check if the command produces different results, so it invalidates the cache if any file used by the command changed. This is why input ordering matters.
When a layer is invalidated, ALL layers after it also rebuild, even if they have not changed. A single change at the top of your Dockerfile can invalidate the entire build chain below it.
Ordering for Cache
Order Dockerfile instructions from least-changing to most-changing. System dependencies change rarely, application dependencies change occasionally, and source code changes frequently. Put each category in its own layer.
# 1. Base image (changes rarely)
FROM node:20-alpine
# 2. System dependencies (changes rarely)
RUN apk add --no-cache tini
# 3. Application dependencies (changes sometimes)
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
# 4. Source code (changes frequently)
COPY . .System packages install first (rarely change). Dependencies install next (change when package.json changes). Source code copies last (changes on every commit). This maximizes cache hits during development.
Diagnosing Cache Misses
When builds are slow, the first step is to identify which layers are rebuilding and why. Docker provides build output that shows cache usage. Understanding this output helps you restructure your Dockerfile for better caching.
# Build with verbose output
docker build --progress=plain -t myapp .
# Output shows cache status:
# => [internal] load build definition from Dockerfile
# => => CACHED [stage-0 1/5] FROM node:20-alpine
# => => CACHED [stage-0 2/5] WORKDIR /app
# => => CACHED [stage-0 3/5] COPY package*.json ./
# => => CACHED [stage-0 4/5] RUN npm ci --production
# => => not cached [stage-0 5/5] COPY . . ← rebuilds hereThe --progress=plain flag shows which layers are cached and which rebuild. Use this to identify unexpected cache invalidation.
Cache from Registry
For CI/CD pipelines, local cache is lost on each clean build. Docker BuildKit supports pulling cache from a registry, so builds on different machines can share cached layers. This dramatically speeds up CI builds.
# Export cache to registry after build
docker build --cache-to type=registry,ref=myregistry/myapp:cache \
-t myapp .
# Pull cache from registry during build
docker build --cache-from type=registry,ref=myregistry/myapp:cache \
-t myapp .The cache-from flag tells BuildKit to check the registry for cached layers. Cache-to exports the build cache after a successful build. This is essential for fast CI pipelines.
BuildKit is the default builder in Docker 23.0+. It provides better cache handling, parallel builds, and advanced features like cache mounts. Enable it with DOCKER_BUILDKIT=1 or ensure you are using a recent Docker version.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.