BuildKit, cache and multi-platform images
Buildx builders, cache mounts that skip dependency downloads, registry-backed caches for CI, and building for other architectures.
BuildKit and builders
BuildKit is the build engine behind modern Docker. The docker driver builds inside the daemon and cannot export cache or cross-compile; the docker-container driver runs a build container with the full feature set, which is what you want for multi-platform work.
docker buildx ls
docker buildx create --name builder --driver docker-container --use
docker buildx inspect --bootstrap # confirms the driver and platforms
# a Dockerfile can demand a newer frontend for the latest syntax
cat > Dockerfile <<'EOF'
# syntax=docker/dockerfile:1
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]
EOF
docker buildx build -t app:dev --load . # --load puts it in the local image store
docker buildx du # cache usage of the current builder- With the
docker-containerdriver the result stays in the builder cache until you ask for it: use--loadfor the local daemon or--pushfor a registry. - BuildKit builds independent stages in parallel and only runs the steps the final image needs.
docker buildx prunereclaims builder cache; the classicdocker system prunedoes not touch it.
Cache mounts and cache backends
| Mount | Purpose | Ends up in the image? |
|---|---|---|
type=cache | Persistent scratch space for package managers | No |
type=bind | Read source or config from the build context | No |
type=tmpfs | Scratch data held in memory | No |
type=secret | A credential available only during that RUN | No |
COPY | Real file content in a layer | Yes — permanently |
# python
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt
# apt: the lists directory is reused between builds
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends curl
# go modules
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=bind,source=go.sum,target=go.sum \
go mod download# reuse layers across machines and CI runs
docker buildx build \
--cache-from type=registry,ref=registry.example.com/team/app:buildcache \
--cache-to type=registry,ref=registry.example.com/team/app:buildcache,mode=max \
--tag registry.example.com/team/app:1.4.2 --push .
# GitHub Actions cache, no registry needed
docker buildx build --cache-from type=gha --cache-to type=gha,mode=max --load .
# local directories, useful on one machine
docker buildx build --cache-from type=local,src=/tmp/cache --cache-to type=local,dest=/tmp/cache .💡
A cache mount is a build-time optimisation, not storage: nothing written to it is in the image, so you never need to clean it and you must not rely on it being empty. Keep the instruction that populates it above
COPY . . so a source edit does not invalidate it.Building for more than one architecture
# one command, one tag, both architectures, straight to the registry
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag registry.example.com/team/app:1.4.2 \
--provenance=true --sbom=true \
--push .
docker buildx imagetools inspect registry.example.com/team/app:1.4.2
# multi-platform cannot use --load: the local store holds one platform per tag
docker buildx build --platform linux/arm64 -t app:arm64 --load .
# native builders in CI are far faster than emulation
docker buildx create --name arm --driver docker-container --node arm --platform linux/arm64--platformwith more than one value requires--push(or--output); BuildKit produces a manifest list, which a local image store cannot represent.- The second architecture is built under QEMU emulation unless you have a native node, and it can be several times slower — CI minutes add up quickly.
--provenanceand--sbomattach attestations to the manifest, which scanners and admission controllers can then verify.- Only the stages the final image depends on are executed per platform, so a multi-stage build keeps the cross-build cost manageable.
FAQ
Why is 'RUN --mount=type=cache' rejected as an unknown flag?
The Dockerfile is being parsed by an older frontend, or you are on the legacy builder. Add
# syntax=docker/dockerfile:1 as the first line and build with BuildKit (default since Docker 23, and DOCKER_BUILDKIT=1 before that).Are cache mounts shared between parallel builds of the same project?
Yes, and concurrent writers can conflict. Add
sharing=locked to serialise access, which is what package managers like apt need, or use sharing=private for isolation.Related
Registries, tags and image distribution Docker in CI and publishing images
Last refreshed 2026-09-18.