Building Multi-Architecture Images (amd64 + arm64)
Apple Silicon laptops and Graviton servers mean arm64 is no longer optional -- ship a manifest that covers both.
Your team may develop on arm64 Macs while production runs on amd64 (or the reverse). A multi-architecture image lets a single tag serve the right binary to each platform. This lesson covers the two ways to build them in CI and their trade-offs.
What a multi-arch image is
A multi-arch image is really a manifest list pointing at one image per platform. When a host pulls the tag, the registry serves the variant matching its architecture. You build all variants and push them under one tag.
Building with buildx
buildx builds multiple platforms in one command and pushes a combined manifest. On a single-arch runner you enable QEMU emulation first.
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ghcr.io/acme/myapp:ciEmulation vs native runners
- QEMU emulation builds every platform on one runner but is slow for the emulated arch.
- Native runners (one amd64, one arm64) build each variant at full speed, then merge manifests.
- A faster managed arm64 runner removes the emulation tax entirely for arm builds.
- For compiled languages, cross-compiling in a single stage can beat emulation.
Merging native builds
When you build each arch on its own runner, push by-digest and combine them into one tag with imagetools.
docker buildx imagetools create -t ghcr.io/acme/myapp:ci \
ghcr.io/acme/myapp@${AMD64_DIGEST} \
ghcr.io/acme/myapp@${ARM64_DIGEST}Key takeaways
- A multi-arch image is a manifest list serving the right variant per platform.
- buildx with QEMU builds all platforms on one runner, but emulation is slow.
- Native per-arch runners build faster, then merge with buildx imagetools create.