sehaj/learn
Docker/07 Production

07.04 · with code · 10 min

Multi-platform builds

Build one image tag that runs on both Intel/AMD servers and ARM machines, and understand the emulation that makes it possible.

The core idea

Images contain compiled code for one CPU architecture. Apple Silicon Macs and many cloud servers (AWS Graviton, for example) are linux/arm64; most other servers are linux/amd64. docker buildx can build for a platform other than your own, or for several at once, publishing them under one tag as an index so each machine pulls the right one.

Build for another platform

On an arm64 Mac:

❯terminal
$ docker buildx build --platform linux/amd64 -t notes-api:amd64 --load .
$ docker image inspect -f '{{.Os}}/{{.Architecture}}' notes-api:amd64 notes-api
linux/amd64
linux/arm64
$ docker run --rm --platform linux/amd64 --entrypoint node notes-api:amd64 -p 'process.arch'
x64

The amd64 build took 22 seconds here instead of a few, and the container still ran. Both are thanks to QEMU emulation: Docker Desktop translates amd64 instructions to arm64 on the fly. It works, but it's slow, so heavy builds (compiling native modules, big npm cis) under emulation can take many times longer. On Linux, install emulation with the tonistiigi/binfmt image or docker/setup-qemu-action in CI.

Build for both at once

❯terminal
$ docker buildx build --platform linux/amd64,linux/arm64 -t localhost:5050/notes-api:multi --push .
#26 pushing manifest for localhost:5050/notes-api:multi@sha256:7804d1e8406c...
$ docker buildx imagetools inspect localhost:5050/notes-api:multi
Name: localhost:5050/notes-api:multi
MediaType: application/vnd.oci.image.index.v1+json
Name: localhost:5050/notes-api:multi@sha256:917b5acd462e...
Platform: linux/amd64
Name: localhost:5050/notes-api:multi@sha256:a0e6a0ef445c...
Platform: linux/arm64
Name: localhost:5050/notes-api:multi@sha256:d06733d4f30e...
...

Output trimmed (the local registry is from 02.06 Tags, digests and registries). One tag, an index, two images. The third entry is an attestation: provenance metadata BuildKit attaches describing how the image was built. Its platform shows as unknown/unknown, and Docker ignores it when choosing what to run.

Multi-platform images usually go straight to a registry with --push. Loading them into your local image store with --load needs the containerd image store, which recent Docker Desktop versions use by default.

Faster than emulation

  • Build natively per platform in CI (amd64 and arm64 runners) and merge the results; fastest for heavy builds.
  • Cross-compile in the build stage for languages that support it (Go, Rust): run the build stage on the native platform with FROM --platform=$BUILDPLATFORM and compile for $TARGETPLATFORM. No emulation at all.
  • For the notes API, emulation is fine: the only slow part is npm ci, and the cache (07.05 Build cache in CI) skips it most of the time.
Predict first

You push an image built on an M-series Mac with plain docker build to a registry. An amd64 server pulls it and the container exits immediately with 'exec format error'. What's the fix?

Commit to an answer before you read on.
CheckpointBuild for the server you'll deploy to

Build the notes API for linux/amd64 and check the architecture with docker image inspect. Then build for both platforms, push to the local registry from 02.06 Tags, digests and registries, and inspect the index with docker buildx imagetools inspect.

STUCK? ▸

"multiple platforms feature is currently not supported for docker driver": switch to the containerd image store in Docker Desktop's settings, or create a builder with docker buildx create --use --driver docker-container.

Recap

Interview questions

MidfreqGood to knowConcept

How do multi-architecture Docker images work?

Report a mistakeEdit this page