# Docker BuildKit and buildx: Cache Mounts, Secrets and Multi-Platform Images

> Speed up Docker builds with BuildKit cache mounts, use build secrets safely and build multi-platform images (amd64 and arm64) with buildx and Rancher Desktop.

- Source: https://www.itwonderlab.com/docker-buildkit-buildx/
- Published: 2026-10-06
- Updated: 2026-10-06
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## What is BuildKit

**BuildKit** is the engine that builds your images. It builds independent stages in parallel, skips stages nobody needs, transfers only the files that changed and adds features that the old builder did not have: cache mounts, secret mounts, SSH forwarding and multi-platform builds. It is the default in current Docker, and Rancher Desktop uses it for both `docker build` and `nerdctl build`.

`docker buildx` is the CLI front end for the extra features. Features such as `RUN --mount` need nothing special in the command, only in the `Dockerfile`.

## Cache mounts: reuse downloads between builds

Package managers download the same files on every build when a dependency changes. A **cache mount** gives a `RUN` instruction a persistent directory that is not saved in the image:

```dockerfile title="Dockerfile (Python)"
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
```

Change one line of `requirements.txt` and `pip` finds the rest of the packages already downloaded in the cache. The same idea for other tools:

```dockerfile
# Node.js
RUN --mount=type=cache,target=/root/.npm npm ci

# Go
RUN --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build \
    go build -o /out/server .

# apt (Debian): keep the lists and archives in the cache
RUN rm -f /etc/apt/apt.conf.d/docker-clean
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
```

## Secret mounts: never bake a secret into a layer

A token passed with `ARG` or `ENV`, or copied with `COPY`, stays in the image history forever, even if you delete it in a later layer. A **secret mount** makes the secret available to one `RUN` as a file and leaves nothing behind:

```dockerfile title="Dockerfile"
# syntax=docker/dockerfile:1
FROM node:22-slim
WORKDIR /app
COPY package*.json ./
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
    npm ci
COPY . .
```

```shell
$ docker build --secret id=npmrc,src=$HOME/.npmrc -t myapp .
```

Check that nothing leaked:

```shell
$ docker history --no-trunc myapp | grep -i npmrc    # nothing
```

For private Git repositories use an **SSH mount**: `RUN --mount=type=ssh git clone git@github.com:org/private.git` and build with `docker build --ssh default .`.

## Multi-platform images

Your Apple Silicon laptop builds `arm64` images, but your [EKS](https://www.itwonderlab.com/aws-eks/) nodes or [Fargate](https://www.itwonderlab.com/aws-fargate/) tasks may run `amd64`. Running the wrong architecture fails with `exec format error`. Build the platform you deploy to, or both:

```shell
$ docker buildx build --platform linux/amd64 -t myapp:1.0 .
$ docker buildx build --platform linux/amd64,linux/arm64 -t registry.example.com/myapp:1.0 --push .
```

There are three ways to produce the other architecture:

1. **QEMU emulation**: no changes to the Dockerfile, but much slower, especially for compilation. Install the emulators once if your system does not have them: `docker run --privileged --rm tonistiigi/binfmt --install all`.
2. **Several native builders**: one `amd64` node and one `arm64` node joined in a single builder, the fastest option for CI:

   ```shell
   $ docker buildx create --use --name mybuild ssh://user@amd64-host
   $ docker buildx create --append --name mybuild ssh://user@arm64-host
   ```

3. **Cross-compilation** in a multi-stage Dockerfile, which needs no emulation at all:

   ```dockerfile
   FROM --platform=$BUILDPLATFORM golang:1.24 AS build
   ARG TARGETOS
   ARG TARGETARCH
   WORKDIR /src
   COPY . .
   RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o /out/server .

   FROM gcr.io/distroless/static-debian12:nonroot
   COPY --from=build /out/server /server
   ENTRYPOINT ["/server"]
   ```

   The build stage runs natively on your machine (`$BUILDPLATFORM`) and only the output targets another platform.

> [!NOTE]
> A multi-platform result is a manifest list that the classic local image store cannot hold. Push it with `--push`, or use an engine whose image store supports manifest lists (current Docker Engine and Docker Desktop use the containerd image store by default). If `docker buildx build --platform a,b` complains, create a builder with `docker buildx create --driver docker-container --bootstrap --use` and push to a registry.

## Cache in CI

Local caches vanish on a clean CI runner. Export the build cache to a registry so the next build can import it:

```shell
$ docker buildx build \
    --cache-from type=registry,ref=registry.example.com/myapp:buildcache \
    --cache-to type=registry,ref=registry.example.com/myapp:buildcache,mode=max \
    -t registry.example.com/myapp:1.0 --push .
```

## Check your Dockerfile

```shell
$ docker build --check .      # lints the Dockerfile with the built-in rules
```

## Next steps

Make sure what you ship is safe: [Docker image security and scanning](https://www.itwonderlab.com/docker-image-security-scanning/).
