Docker Image Layer Ordering for Maximum Cache Reuse
Quick answer
Docker reuses a cached layer only if the instruction and everything before it are unchanged, so the order of Dockerfile instructions decides how much work a...
Docker reuses a cached layer only if the instruction and everything before it are unchanged, so the order of Dockerfile instructions decides how much work a rebuild repeats. Put the things that change rarely first, such as the base image and dependency installation, and the things that change often last, such as application source. A good order can turn a multi-minute rebuild into a few seconds.
How Does the Docker Build Cache Decide What to Reuse?
Each instruction in a Dockerfile produces a layer. For every instruction, Docker checks whether it already has a cached layer built from the same parent layer with the same instruction. For COPY and ADD, it also compares the contents of the files being copied, usually through checksums.
The critical rule is that invalidation cascades. When one layer changes, every layer after it must be rebuilt, even if those later instructions have not changed. That is why a change to a single source file can trigger a full reinstall of dependencies if the instructions are in the wrong order.
Think of the Dockerfile as a pipeline sorted by how often each step changes. The less volatile a step, the earlier it belongs.
What Is the Right Order for a Typical Application?
A reliable pattern is to copy only the dependency manifests first, install dependencies, and then copy the rest of the source. Dependencies change much less often than code, so that expensive layer stays cached across most builds.
FROM node:20-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
CMD ["node", "dist/server.js"]
The same idea applies in other ecosystems. In Python, copy requirements files first. In Go, copy go.mod and go.sum, run the module download, and then copy the code. In each case, the install step is cached until the manifest changes.
- Base image and system packages first, since they rarely change.
- Dependency manifests and installation next.
- Application source and build steps after that.
- Metadata such as labels and build arguments at the end, since they often change per build.
Which Mistakes Destroy Cache Reuse?
A few habits repeatedly break caching. The most common is a broad COPY . . placed early, which invalidates everything after it whenever any file in the build context changes, including documentation or test files.
- Placing build arguments such as a commit SHA or timestamp near the top, so every build produces a new layer chain.
- Running apt-get update in a separate instruction from install, which can serve stale package lists from the cache.
- Copying files that change constantly, such as logs or local build output, because the context was not trimmed.
- Splitting related commands unnecessarily, or combining unrelated ones, so that a small change forces a large rebuild.
Use a .dockerignore file to keep irrelevant files out of the build context. This both speeds up context transfer and prevents unrelated edits from invalidating COPY layers.
How Do You Balance Layer Count and Image Size?
Each RUN, COPY, and ADD creates a layer, and files deleted in a later layer still occupy space in earlier ones. Combine commands that belong together, and clean up temporary files in the same instruction that created them.
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates curl \
&& rm -rf /var/lib/apt/lists/*
Multi-stage builds help as well. Heavy build tools live in an early stage, and only the final artifacts are copied into a small runtime stage. Cache behavior is tracked per stage, so you can keep the expensive stage stable while the final stage changes often.
Modern builders also offer cache mounts and exporting caches between CI runs. These features depend on your builder version, so consult the BuildKit documentation before relying on them.
Frequently Asked Questions
Why does my build reinstall dependencies on every change?
Probably because the source is copied before the install step, or because a changing file sits in an earlier COPY. Reorder so only manifests precede the install.
Does the order of RUN commands matter?
Yes. A change in an earlier instruction invalidates all later ones, so place the most stable commands first.
Does .dockerignore affect caching?
Yes. Excluding files that change often keeps COPY layers stable and reduces the size of the build context.
Is fewer layers always better?
Not always. Fewer layers can reduce overhead, but combining unrelated steps hurts cache reuse. Group by how often things change.
Summary
Order instructions from least to most frequently changing, copy manifests before source, trim the build context, and avoid volatile values near the top. Check the BuildKit documentation for caching features in your version.