Docker

Docker Layer Caching Order for Faster Dependency Installs

4 min read by DebuggedIt

Quick answer

Quick answer: Docker caches each Dockerfile instruction as a layer and invalidates every layer after the first one that changes. Put rarely changing steps...

Quick answer: Docker caches each Dockerfile instruction as a layer and invalidates every layer after the first one that changes. Put rarely changing steps first, such as the base image and system packages, then copy only your dependency manifests and install dependencies, and copy the application source last. That way a code edit reuses the cached dependency layer instead of reinstalling everything.

How Does Docker Layer Caching Decide What to Rebuild?

Each RUN, COPY, and ADD instruction produces a layer. Before building a step, Docker checks whether it already has a layer created from the same parent with the same instruction. For COPY and ADD it also compares the contents of the files being copied, using checksums rather than timestamps, so touching a file without changing it does not break the cache.

The key rule is that a cache miss cascades. When one layer must be rebuilt, every layer after it is rebuilt as well, even if its own instruction did not change. This is why instruction order is the most important factor in build speed. A slow step placed after a frequently changing step is rebuilt on almost every build.

RUN instructions are cached by their command text, not by what the command would produce today. A RUN apt-get update line stays cached even though upstream packages have moved on, which is why update and install commands belong in a single RUN. It is also why a periodic build without cache is useful when you need fresh security updates in the base layers.

What Is the Right Order for a Typical Dockerfile?

The pattern is the same across languages: copy the files that define dependencies, install, and only then copy the rest. Here is the shape for a Python service:

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "-m", "myapp"]

Applying the same idea in any stack gives a simple ordering checklist:

  • Base image and system packages first, because they change least often.
  • Dependency manifests and lock files copied on their own, before any install step.
  • The dependency install step, which is usually the slowest and benefits most from caching.
  • Application source code last, since it changes on nearly every commit.
  • Build arguments that change often, such as a version or commit hash, as late as possible.

Pair this with a .dockerignore file. Excluding directories like .git, local virtual environments, and build output keeps the build context small and prevents irrelevant file changes from invalidating the COPY step that copies your source.

How Do You Keep the Cache Effective in CI?

CI runners frequently start with an empty cache, so every pipeline rebuilds every layer and the ordering work seems to do nothing. The fix is to persist the cache between runs. Modern BuildKit-based builders can export and import cache to a registry or to storage provided by your CI system. The exact flags and supported backends depend on your builder version, so check its documentation.

Cache mounts are another useful tool. A RUN instruction can mount a persistent directory for a package manager cache, so downloads are reused across builds without ending up inside the final image. This helps most when a lock file changes slightly and the full install layer has to rebuild anyway.

Multi-stage builds also improve caching because each stage is cached independently. Keep the build stage for compilers and dependency installation, and copy only the results into a small runtime stage. Watch for common cache breakers: placing an ARG that changes on every build before the install step, copying generated files, and using broad COPY instructions that sweep in files that change constantly.

Frequently Asked Questions

Why does Docker rebuild everything after a small change?

Because a cache miss invalidates all later layers. If a changed file is copied before the install step, the install runs again. Move that COPY later in the Dockerfile.

Should I combine RUN commands into one?

Combine commands that must stay consistent, such as package index update and install. Do not merge unrelated steps that change at different rates, because that reduces cache reuse.

How do I force Docker to ignore the cache?

Pass the no-cache option to the build command. Use it occasionally to pick up updated base layers and security patches, not on every build.

Does a bigger build context slow caching?

Yes. Docker must send and checksum the context, so a large context slows builds. Use a .dockerignore file to exclude files the image does not need.

Summary

Order your Dockerfile from least to most frequently changing, install dependencies from manifests before copying source, and keep the build context lean. Persist cache across CI runs and use multi-stage builds to isolate work. Confirm cache options and flags in the documentation for your builder version.