← Back to Blog

Optimizing Multi-Architecture Docker Builds with Buildx

How I built a reliable WhatsApp AI shopping assistant for Clickmothercare that survives hallucinated products, silent save failures, and multi-agent handoff bugs.

Anas Rhimi
Anas Rhimi August 2026 • 8 min read

With the proliferation of ARM-based servers (AWS Graviton, Ampere Altra) and developer machines (Apple Silicon), shipping multi-architecture container images is no longer optional. Docker Buildx, powered by BuildKit, simplifies this, but unoptimized QEMU emulation can lead to agonizingly slow CI pipelines. Let's optimize multi-arch builds.

Setting up a Remote ARM64 Builder

Instead of relying entirely on software emulation (QEMU) on an x86 host, which incurs massive CPU overhead, we can configure Buildx to distribute the build load across native nodes.

# Create a new builder instance
docker buildx create --name multi-arch-builder --use

# Add a native x86_64 node (usually the local CI runner)
docker buildx create --name multi-arch-builder \
  --append \
  --node x86_node \
  --platform linux/amd64

# Add a remote native ARM64 node via SSH
docker buildx create --name multi-arch-builder \
  --append \
  --node arm64_node \
  --platform linux/arm64 \
  ssh://ci-user@arm-builder.local

Annotation: By appending a remote SSH node, BuildKit smartly routes linux/arm64 jobs to the native ARM hardware and linux/amd64 to the local runner. This eliminates emulation latency entirely. Ensure SSH keys are configured without passphrases for the CI runner.

Optimizing the Dockerfile for Cross-Compilation

If remote native nodes aren't available, cross-compilation within the Dockerfile is significantly faster than QEMU emulation. Here is an example for a Go application using Buildx injected variables.

# Use the native build platform (e.g., amd64 CI runner)
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder

# Buildx injects these arguments automatically
ARG TARGETPLATFORM
ARG TARGETOS
ARG TARGETARCH

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
# Leverage Go's native cross-compilation instead of QEMU
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
    go build -o /api-server main.go

# The final stage uses the target platform
FROM --platform=$TARGETPLATFORM alpine:latest
COPY --from=builder /api-server /api-server
ENTRYPOINT ["/api-server"]

Annotation: FROM --platform=$BUILDPLATFORM ensures the compiler runs on the host's native architecture (fast). We then pass the intended TARGETOS and TARGETARCH variables to the compiler. The compiler itself outputs a binary for the target architecture, bypassing the need to emulate the build environment.

Efficient Caching Strategy

BuildKit supports registry-based caching, which is critical for ephemeral CI runners (like GitHub Actions) to avoid rebuilding unchanged layers across architectures.

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --tag myrepo/api-server:latest \
  --cache-from type=registry,ref=myrepo/api-server:buildcache \
  --cache-to type=registry,ref=myrepo/api-server:buildcache,mode=max \
  --push \
  .

Annotation: mode=max caches all intermediate layers, not just the final image layers, significantly accelerating future builds. The cache is pushed to the remote registry alongside the image, decoupling the cache state from the CI runner's local filesystem.

Is your AI agent's infrastructure secure and reliable?

Book a Free 15-Min Technical Audit
Hire Me