Containers·8 min read·

Building Multi-Arch Images with Docker Buildx

Build one image tag for amd64 and arm64 with Docker Buildx, push a single manifest, and verify every platform with buildx imagetools inspect.

NB

Netbay Developer Relations

Netbay Engineering

On this page

A Docker image that only runs on x86 is a deployment constraint disguised as a default. ARM servers are no longer a curiosity — they run web tiers, CI runners, and increasingly production workloads — and a single image tag that carries variants for both architectures is what lets one compose file and one command install the same app everywhere. Buildx, the modern image builder shipped with current Docker, turns "compile for every architecture you might need" into one instruction with a manifest that resolves per-platform at pull time. This guide walks through creating the builder, building the matrix, pushing the manifest, and verifying what you actually shipped.

One tag, many platforms buildx builder multi-platform one build amd64 x86 variant arm64 arm variant MANIFEST tag app:1.0 pull picks arch Ship one name; the client pulls what it can run

Why a Single Tag for Both Architectures

With a normal build and push, the tag app:2.0 points at exactly one platform. A multi-arch push publishes a manifest list: the registry keeps every variant, the tag references the whole list, and each client pulls the variant matching its own architecture automatically. The user experience becomes invisible plumbing — docker pull, compose up, and image references in scripts all just work on both CPUs. The publishing side is where the work actually happens, and buildx is the tool that makes pushing the whole matrix a single command.

Create and Select the Builder

Buildx ships with modern Docker as the default builder, and the docker buildx is the diagnostic verb that shows what is available and active:

bash
# Check the current builder and platforms it can run
docker buildx ls

# Create a dedicated multi-platform builder
docker buildx create --name multi --use
docker buildx ls

The default docker-container builder can hand multiple architectures to a single build, while the legacy builder cannot. One builder per machine is enough; recreate it if you ever switch Docker storage roots. Note that --use makes it the active builder for subsequent buildx invocations in this shell.

Build the Whole Matrix in One Command

The pattern is one command, two platforms, one tag, and the --push flag, which uploads every variant and the manifest in a single pass.

bash
docker buildx build   --platform linux/amd64,linux/arm64   -t registry.example.org/app:2.0   --push   .

The build compiles the Dockerfile once per listed platform — each variant runs its own stage graph — and the registry ends up with amd64 and arm64 artifacts plus a manifest that recognizes both. Any platform Docker can target can be added to the list; the comma-separated matrix is the whole configuration. Add --provenance=false only when a registry has trouble with BuildKit's provenance metadata; otherwise leave it on.

Verification: Prove What You Published

Trusting the tag is how wrong-platform incidents start. The manifest is inspectable without pulling a single layer:

bash
docker buildx imagetools inspect registry.example.org/app:2.0
docker buildx imagetools inspect --raw registry.example.org/app:2.0
docker run --rm --platform linux/arm64   registry.example.org/app:2.0 uname -m

The inspect output lists each platform with its digest, so a missing arm64 variant is caught before anyone deploys. The raw variant dumps the manifest JSON itself for the truly suspicious. The docker run line proves behavior on a specific platform — if your host is x86, running the arm64 variant requires emulation, which is exactly the next caveat.

Emulation vs. Native Builds, and the One-Platform Load

Building foreign platforms uses binary-translation emulation under the hood unless a native runner is registered. It works, and for most Dockerfiles the only cost is compile time, but emulated builds are measurably slower and occasionally expose bugs in scripts that assume the host architecture. Organized builds use the matrix build on one machine (as above) or farm each platform to a matching native runner. The same rule governs local pulls: docker buildx build --load imports a build into the local image store, but only for the host's own platform — load is a per-platform verb, so it pairs with a single --platform.

bash
# Load the host's own variant for local runs
docker buildx build --platform linux/amd64 --load -t app:dev .
docker buildx build --platform linux/arm64 -t registry.example.org/app:2.0 --push .

The contrast between the two lines is the mental model: --load keeps a copy locally for the architecture you are standing on, --push publishes the full matrix to the registry where every client does the resolving.

Takeaway

Multi-arch publishing collapses an entire architecture matrix into one manifest and one tag: build with a buildx builder, push both platforms at once, and verify with imagetools inspect before anything deploys. The payoff compounds the moment a single compose file runs identically on x86 and ARM hosts with no conditional tags and no divergence between images. On a Netbay VPS, publishing a multi-arch tag means every future host — whatever architecture it turns out to use — pulls the right variant the first time, no churn on your end — netbayhosts.in.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance