Building multi-arch images with buildx and QEMU
When your team mixes Apple Silicon and x86 hardware, you need one published image tag that resolves to the right architecture on each host. docker buildx with QEMU emulation builds both architectures and pushes them as a single manifest list. This page is the concrete build-and-publish procedure behind the Multi-Architecture Builds for ARM & x86 cluster.
Prerequisites
- Docker Engine 24+ with
buildx - Push access to a registry that supports manifest lists (GHCR, ECR, Docker Hub)
- A
Dockerfilethat referencesTARGETARCH/TARGETOSfor any binary download
How-To Steps
-
Register QEMU binfmt handlers so the daemon can emulate the foreign architecture:
docker run --privileged --rm tonistiigi/binfmt --install all docker buildx ls # confirm linux/amd64 and linux/arm64 are listed -
Create and select a buildx builder backed by the container driver:
docker buildx create --name multiarch --driver docker-container --use docker buildx inspect --bootstrap -
Build both architectures and push the manifest in one command:
docker buildx build \ --platform linux/amd64,linux/arm64 \ --tag ghcr.io/org/dev-image:1.4.0 \ --cache-to type=registry,ref=ghcr.io/org/dev-image:buildcache,mode=max \ --cache-from type=registry,ref=ghcr.io/org/dev-image:buildcache \ --push . -
Resolve the manifest digest and pin it in
devcontainer.json:docker buildx imagetools inspect ghcr.io/org/dev-image:1.4.0 --format '{{.Manifest.Digest}}'{ "image": "ghcr.io/org/dev-image:1.4.0@sha256:9f2b...", "remoteUser": "vscode" } -
Verify both architectures are present:
docker buildx imagetools inspect ghcr.io/org/dev-image:1.4.0 # lists amd64 and arm64
Common Pitfalls
| Symptom | Root Cause | Remediation |
|---|---|---|
buildx can’t target arm64 | QEMU binfmt not installed | Run tonistiigi/binfmt --install all |
--push required error | Default builder can’t export manifest lists | Use a docker-container driver builder |
| Build extremely slow | Emulating the non-native arch | Build each arch on a native runner, merge with imagetools create |
| Tag becomes single-arch | A later docker push overwrote the manifest | Always publish via buildx --platform |
Conclusion
The invariant: one buildx build --platform invocation produces the manifest list, and you pin its digest. Reserve QEMU for convenience builds; for speed, build natively per architecture in CI and merge. Either way, the published tag stays multi-arch and reproducible.
FAQ
Do I need a separate builder, or will the default do?
The default docker driver can’t export manifest lists. Create a docker-container driver builder with buildx create to push multi-arch images.
How do I avoid emulation slowness in CI?
Run an amd64 job and an arm64 job on native runners, build single-arch images, then combine them with docker buildx imagetools create -t tag amd64-ref arm64-ref.
Related
- Multi-Architecture Builds for ARM & x86 — the parent cluster.
- Pinning base image digests with SHA256 — pinning the manifest you publish.
- Cross-compiling Go binaries inside containers — the language-level counterpart to image-level multi-arch.