Docker BuildKit Cache Mount Generator

Add RUN --mount=type=cache to the steps that download the same things every build. The commonest attempt at this has no effect at all, and the reason is not visible anywhere.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Generate. Nothing leaves this tab.

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

An apt install

The mount needs two extra lines first, or it catches nothing

RUN apt-get update && apt-get install -y build-essential

A pip install with --no-cache-dir

The flag and the mount are opposites, and the flag wins

RUN pip install --no-cache-dir -r requirements.txt

An npm install

Straightforward, and the cache still lives in the builder rather than the image

RUN npm ci

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Adding an apt cache mount without removing docker-clean

    The base image deletes the downloaded packages after every install, so the mount holds nothing and every build downloads the same .debs again. Nothing reports it.

    Instead:Remove /etc/apt/apt.conf.d/docker-clean and set Keep-Downloaded-Packages first.

  2. Measuring the speed-up locally and expecting it in CI

    The cache lives in the builder. An ephemeral runner has no builder from last time, so it starts cold on every job.

    Instead:Use a persistent builder, or export the cache with --cache-to and --cache-from.

  3. Leaving --no-cache-dir on the install command

    That flag exists to keep the cache out of the layer. With a mount it just prevents anything being cached at all.

    Instead:Remove it when you add the mount. The two are solving the same problem in opposite ways.

The Debian images delete the apt cache after every install

/etc/apt/apt.conf.d/docker-clean ships in the debian and ubuntu base images and removes the downloaded .deb files at the end of each apt-get run. A cache mount over /var/cache/apt catches nothing until that file is gone.

Two lines make the apt cache real

Remove docker-clean and set Binary::apt::APT::Keep-Downloaded-Packages to true. Without both, the mount is present, the build looks correct, and every build downloads everything again. This is the single most common reason a cache mount appears not to work.

The cache is in the builder, not in the image

Which is the point: none of it becomes a layer. It also means the cache belongs to the machine that ran the build. A fresh CI runner starts cold every time, so the speed-up measured on a laptop is not the speed-up CI gets. For that you need a persistent builder, or --cache-to and --cache-from pointing at a registry.

sharing=locked is the safe default

The default is shared, which lets parallel builds write the same directory at once. Most package managers are not safe under that and leave corrupt partial downloads that then persist. locked makes the second build wait; private gives each build its own copy at the cost of the sharing.

The no-cache flags are the opposite of a cache mount

apk add --no-cache, pip install --no-cache-dir and their equivalents exist to keep the cache out of the layer. With a mount, the cache is already out of the layer, and the flag just stops anything being written. Remove it when you add the mount.

id= separates one project's cache from another's

Without one the key is the target path, so every image built on that machine with a /root/.npm mount shares a directory. Usually harmless, occasionally not, when two projects need different registries.

It needs BuildKit, and fails loudly without it

With DOCKER_BUILDKIT=0 the --mount flag is a syntax error rather than an ignored option, so the build stops. Adding # syntax=docker/dockerfile:1 at the top makes the requirement explicit.

More docker tools

Docker Container Image Reference Parser Is that a registry or a Docker Hub user? Dockerfile Linter What this image will do to you later docker run to Kubernetes YAML And the flags that have no equivalent Docker Compose to Kubernetes And why depends_on has no equivalent Docker Logging Driver Generator json-file never rotates Dockerfile Non-Root USER Generator Numeric, or Kubernetes rejects it Docker OCI Image Labels Generator One of them actually does something Docker Hub Pull Rate Limit Calculator Counted per IP, not per person Docker Registry Storage Calculator Deleting a tag frees nothing Docker Build Context Analyzer All of it uploads before anything runs Docker Compose to docker run Converter Names stop resolving Docker Compose Version Migrator version: is obsolete now Docker Compose Network and Port Viewer Everything reaches everything Docker Command Explainer Which flags hand over the host Docker BuildKit Secret Mount Generator ARG is in docker history forever Docker .dockerignore Pattern Tester *.log does not match logs/app.log Docker Image Manifest Viewer unknown/unknown is not broken Docker Image Config Viewer Anyone who can pull can read your Env Docker Registry Token Decoder Check the scope before the credentials Docker HEALTHCHECK Generator Docker does nothing when it fails Docker Network Subnet Calculator 31 networks, then it stops Docker History Analyzer The rm did not save anything Docker Run to Compose Converter Some flags have no equivalent Docker CMD and ENTRYPOINT Tester Why docker stop takes ten seconds Docker Port Mapping Tester -p reaches past your firewall Docker -v to --mount Converter -v invents missing directories Docker Restart Policy Simulator The backoff resets after ten seconds Docker Exit Code Explainer 137 is two different problems Docker CPU Limit Converter --cpu-shares limits nothing Docker Memory Limit Calculator -m 512m allows a gigabyte Docker Compose Interpolation Tester An unset variable is an empty string Docker Environment Variable Precedence Tester Your shell does not reach the container Docker Compose Override Merge Previewer An override can add a port, never remove one Dockerfile Multi-Stage Build Analyzer Which stages reach the image Dockerignore Validator It is not .gitignore

Elsewhere on the site