Docker BuildKit Cache Mounts: How to Speed Up Builds

A BuildKit cache mount (RUN --mount=type=cache) keeps package-manager and compiler directories across builds, separate from image layer cache. Even when a layer rebuilds, unchanged packages can stay in the mount.

This article covers cache mounts only from the official Dockerfile and Optimize cache docs—not a Docker layer primer.

Where do you use type=cache?

One-line answer: Attach --mount=type=cache,target=<package-cache-path> to RUN steps that install or compile (npm, Go, pip, apt).

# syntax=docker/dockerfile:1
FROM node:latest
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci
COPY . .

Go:

RUN --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build \
    go build -o /app/hello

Apt (needs exclusive access):

RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    apt-get update && apt-get --no-install-recommends install -y gcc

Cache contents persist between builder invocations without invalidating the instruction cache. Builds must still succeed with an empty or overwritten cache; GC may reclaim space. Mount contents are not committed into the final image.

What are id and sharing?

One-line answer: id names distinct cache buckets; sharing is shared (default), private, or locked.

OptionMeaning
idCache identity; defaults to target. Use separate ids per language/stage
targetMount path inside the container (required)
sharing=sharedConcurrent writers (default); fine for many npm/Go caches
sharing=lockedSecond writer waits; required for apt-style lock files
sharing=privateNew mount when multiple writers appear
ro / readonlyRead-only
uid / gid / modeOwnership/mode for a new cache directory

Do not mix npm and Go into one bucket just because targets differ—set explicit ids. Parallel multi-stage apt installs need sharing=locked (or private) to avoid lock contention.

What should you watch in CI?

One-line answer: Ephemeral runners may drop local BuildKit mounts—pair Dockerfile cache mounts with --cache-from / --cache-to, and keep apt on locked.

Checklist:

  1. New VMs each job — local type=cache may vanish with the builder. Export layer cache to registry/GHA while still using cache mounts for package downloads inside the Dockerfile.
  2. Parallel jobs, same id — serialize apt with sharing=locked or give each job its own id.
  3. Empty-cache correctness — docs require the build to work when GC or another build clears the mount.
  4. No secrets in cache mounts — use type=secret for tokens.
  5. Pin syntax# syntax=docker/dockerfile:1 for stable --mount support.

Locally, BuildKit must be enabled (DOCKER_BUILDKIT=1 or buildx).

Wrap-up

Speed package installs with RUN --mount=type=cache → ② pick id/sharing (apt=locked) → ③ add external cache in CI. See RUN —mount=type=cache and Optimize cache usage.

Sources