Docker Build Cache Mounts for Faster CI Dependency Installs
Quick answer
Docker build cache mounts let a RUN instruction reuse a persistent directory, such as a package manager cache, across builds without storing it in the final...
Docker build cache mounts let a RUN instruction reuse a persistent directory, such as a package manager cache, across builds without storing it in the final image. They speed up dependency installs because downloads survive even when the layer itself is invalidated. They require BuildKit, and in CI they only help when the cache is preserved between runs.
How Are Cache Mounts Different From Layer Caching?
Layer caching is all or nothing. If you change package.json or requirements.txt, Docker invalidates that layer and every layer after it, and the entire install runs again from scratch, re-downloading every package.
A cache mount works at a finer level. It attaches a directory to the RUN step that persists on the builder, so the package manager can find previously downloaded files and skip the network. The directory is not part of the image layer, which keeps the image small and avoids shipping cache files to production.
# syntax=docker/dockerfile:1
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
COPY . .
The same pattern applies to other ecosystems, with a different target path for each package manager. Check the tool's documentation for its cache location, because pointing the mount at the wrong directory silently gives you no benefit at all.
Where Do Cache Mounts Help Most?
They help most when dependencies change often but only slightly. Adding one package to a project with hundreds should not trigger a full download. They are also valuable for compiled languages where module or build caches are expensive to recreate.
- Python: the pip cache directory.
- Node.js: the npm or package manager cache directory.
- Go: the module cache and the build cache.
- System packages: the apt cache, with some extra care about locking and cleanup behavior.
Layer caching and cache mounts complement each other. Keep copying dependency manifests before source code so unchanged dependencies still hit the layer cache, and add cache mounts for the cases where the layer is rebuilt. Together they cover both the common case of no dependency changes and the frequent case of small ones.
Why Does the Cache Disappear in CI?
The most common surprise is that CI runners are often ephemeral. A fresh runner starts with an empty BuildKit cache, so the mount has nothing to reuse. The speedup only appears when the cache persists between jobs.
You have several options. Use a persistent or self-hosted builder, export and import the cache through a registry or your CI provider's cache feature, or use a remote builder that keeps state. The exact flags and supported cache backends depend on your BuildKit and Buildx versions, so consult the official Buildx documentation before configuring this.
docker buildx build \
--cache-from type=registry,ref=registry.example.com/app:buildcache \
--cache-to type=registry,ref=registry.example.com/app:buildcache,mode=max \
-t app:latest .
Note that registry cache export covers layer cache. Whether contents of cache mounts are exported depends on your setup, so test it rather than assuming it works. Measure build times before and after, using several consecutive CI runs, so you know whether the change actually pays off.
Pitfalls to Avoid
- Do not rely on files in a cache mount being present in the final image. They are not.
- Do not treat the cache as a source of truth. Builds should still succeed with an empty cache.
- Avoid sharing one cache between very different projects without a cache id, which can cause conflicts.
- Be careful with concurrent builds writing to the same cache. Use the sharing option appropriate for your tool.
Frequently Asked Questions
Do cache mounts work without BuildKit?
No. The RUN --mount syntax requires BuildKit, which is the default builder in current Docker versions. Older setups may need it enabled explicitly.
Do cache mounts make the final image smaller?
Indirectly, yes. Cache files live outside the image layers, so you do not need extra cleanup commands to remove package caches from the image.
Is the cache shared across different Dockerfiles?
It can be, if the same cache id and target are used on the same builder. Set an explicit id when you want to control sharing.
Can cache mounts cause stale dependencies?
The package manager still resolves versions from your lock file. The cache only stores downloaded artifacts, so a lock file keeps results reproducible.
Summary
Cache mounts keep package downloads between builds without bloating the image, and they complement normal layer caching. In CI, make sure the cache actually persists, keep builds correct without it, and verify cache export behavior against the current BuildKit documentation.