BuildKit
1. Architecture
graph TD
CLI["docker buildx / CLI"] --> GW[BuildKit Gateway]
GW --> LLBGEN[LLB IR Generator]
LLBGEN --> SOLVER[DAG Solver]
SOLVER --> W1[Worker: snapshotter]
SOLVER --> W2[Worker: executor]
W1 --> CACHE["Content-addressable<br/>cache store"]
W2 --> SNAP["Overlay snapshotter<br/>overlayfs / fuse"]
SNAP --> IMG["Output: image / OCI"]
- buildkitd: daemon that receives LLB graphs and executes them
- LLB (Low-Level Build): protobuf IR — a DAG of operations (exec, copy, mount)
- Snapshotter: manages layer snapshots (overlayfs, native, fuse-overlayfs)
- Worker: executes each LLB operation in isolation
2. Parallel Stage Execution
Classic builder executes stages sequentially. BuildKit builds the dependency DAG and runs independent stages in parallel.
FROM golang:1.22 AS builder
RUN go build -o /app .
FROM node:20 AS frontend # independent of builder
RUN npm ci && npm run build
FROM alpine AS final
COPY --from=builder /app /app
COPY --from=frontend /dist /static
gantt
title BuildKit parallel vs sequential
dateFormat X
axisFormat %s s
section Classic Builder
base image pull : 0, 3
builder stage : 3, 8
frontend stage : 8, 13
final stage : 13, 15
section BuildKit
builder stage : 0, 5
frontend stage : 0, 4
final stage : 5, 7
BuildKit detects that builder and frontend have no dependency → runs them concurrently.
3. Cache Mounts
Cache mounts persist across builds — the directory is not part of the image layer.
# Go modules cache
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -o /app .
# apt cache
RUN --mount=type=cache,target=/var/cache/apt \
apt-get update && apt-get install -y curl
# npm cache
RUN --mount=type=cache,target=/root/.npm \
npm ci
sequenceDiagram
participant D as Dockerfile RUN
participant C as Cache mount<br/>(host volume)
participant L as Image layer
D->>C: read cached deps
C-->>D: cache hit (fast)
D->>D: build / compile
D->>L: write compiled output only
Note over C,L: Cache mount NOT in layer
Cache scope options:
# shared (default): all builds share the cache
RUN --mount=type=cache,target=/go/pkg/mod,sharing=shared
# locked: exclusive access per build
RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked
# private: each build gets its own copy
RUN --mount=type=cache,target=/go/pkg/mod,sharing=private
4. Secret Mounts
Secrets are available only during the RUN step — never written to any layer.
RUN --mount=type=secret,id=gh_token \
GITHUB_TOKEN=$(cat /run/secrets/gh_token) \
GONOSUMCHECK=* GOFLAGS=-mod=mod \
go mod download
docker buildx build \
--secret id=gh_token,env=GITHUB_TOKEN \
-t myapp:latest .
Verification — secret is absent from history:
docker history myapp:latest # no token visible
docker save myapp:latest | tar xO | strings | grep -c TOKEN # 0
5. SSH Agent Forwarding
For private Git repos without exposing keys:
FROM golang:1.22
RUN mkdir -p -m 0700 ~/.ssh && \
ssh-keyscan github.com >> ~/.ssh/known_hosts
RUN --mount=type=ssh \
git clone git@github.com:myorg/private-repo.git
eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519
docker buildx build \
--ssh default=$SSH_AUTH_SOCK \
-t myapp:latest .
6. Multi-Platform Builds
# Create a multi-platform builder
docker buildx create --name multiarch --driver docker-container --use
docker buildx inspect --bootstrap
# Build for multiple platforms and push
docker buildx build \
--platform linux/amd64,linux/arm64,linux/arm/v7 \
--push \
-t myorg/myapp:v1.0.0 .
BuildKit uses QEMU for cross-compilation when native hardware is unavailable. For Go, prefer CGO_ENABLED=0 with GOARCH set to avoid QEMU overhead:
FROM --platform=$BUILDPLATFORM golang:1.22 AS builder
ARG TARGETOS TARGETARCH
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /app .
7. Inline Cache
Push cache metadata to the registry alongside the image:
# Push image + cache metadata
docker buildx build \
--cache-to type=registry,ref=myorg/myapp:cache,mode=max \
--push -t myorg/myapp:latest .
# Pull cache on next build (CI)
docker buildx build \
--cache-from type=registry,ref=myorg/myapp:cache \
--push -t myorg/myapp:latest .
mode=max exports cache for all intermediate layers (not just final). Use in CI for maximum reuse.
Other cache backends:
# Local filesystem cache
--cache-to type=local,dest=/tmp/buildcache,mode=max
--cache-from type=local,src=/tmp/buildcache
# GitHub Actions cache
--cache-to type=gha,mode=max
--cache-from type=gha
8. Output Types
# Default: image in local Docker daemon
docker buildx build -t myapp:latest .
# OCI tarball (portable)
docker buildx build --output type=oci,dest=./myapp.tar .
# Plain tarball
docker buildx build --output type=tar,dest=./myapp.tar .
# Local directory (extract filesystem)
docker buildx build --output type=local,dest=./out .
# Push directly to registry
docker buildx build --output type=image,push=true -t myorg/myapp:latest .
| Output type | Use case |
|---|---|
image |
Standard Docker image, push to registry |
oci |
OCI-compliant tar, use with skopeo, podman |
tar |
Raw filesystem archive |
local |
Extract build artifacts to host directory |