How can one reference such as alpine:3.20 work on both linux/amd64 and linux/arm64 machines — what does the registry return, and how is the correct variant selected?
answer
- index / manifest list = one entry per platform
- platform{os, architecture, variant}
- client matches, pulls only its variant
- buildx --platform ... --push
- exec format error = wrong-arch single image
basics
~20 sThe tag points at a manifest list (OCI image index): a JSON document listing one manifest digest per platform, each annotated with os/architecture/variant. The client picks the entry matching its own platform and pulls only that image's config and layers.
solid answer
~50 sA multi-platform tag resolves to an **index** (Docker calls it a manifest list, OCI calls it an image index) rather than to a single image manifest. The index is a small JSON document whose `manifests` array holds one descriptor per variant: `{mediaType, digest, size, platform:{os, architecture, variant}}`. On pull, the client sends `Accept` headers advertising the index media types; if the registry returns an index, the client matches its own platform (or `--platform`) against the `platform` fields, then fetches only that manifest, its config and its layers. Nothing else is downloaded. Build side: `docker buildx build --platform linux/amd64,linux/arm64 --push` builds each variant and pushes an index over them. Inspect with `docker buildx imagetools inspect`. The classic failure is a single-arch image pulled on a different architecture: it runs and dies with `exec format error`, because the ELF binaries are for the wrong CPU. Under emulation via QEMU/binfmt it may run, slowly.
code
bash · 6 linesdocker buildx imagetools inspect alpine:3.20
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t registry.example.com/team/app:1.0 \
--push .go deeper
Know that a tag can point at a list of per-platform images and that the client picks the one matching its machine.
Name the artifact (image index / manifest list), the platform fields used for matching, and how buildx produces one.
Cover pinning the index digest rather than a per-platform manifest digest, emulation traps, and diagnosing exec format errors in mixed-architecture fleets.
Frame the fleet strategy: which architectures you publish, native builders versus emulation cost in CI, and how digest pinning policy interacts with multi-arch indexes.
## The problem A container image contains compiled binaries for one CPU architecture and one OS. `alpine:3.20` must nevertheless work on an x86-64 CI runner, an Apple-silicon laptop and an arm64 cloud node. Something has to choose. ## The index (manifest list) The registry solves it with one more level of indirection. A tag may point at an **image index** — media type `application/vnd.oci.image.index.v1+json`, or the older Docker `application/vnd.docker.distribution.manifest.list.v2+json`. Its body is essentially: ``` manifests: [ { digest: sha256:aaa..., platform: { os: linux, architecture: amd64 } }, { digest: sha256:bbb..., platform: { os: linux, architecture: arm64, variant: v8 } }, ... ] ``` Each entry is a descriptor pointing at a normal, single-platform image manifest, which in turn points at that platform's config and layers. ## Resolution at pull time 1. The client requests the manifest for the tag, sending `Accept` headers that list both index and manifest media types. 2. The registry returns whatever the tag points at. Older clients that do not advertise index types may get an error or a fallback single manifest. 3. If it is an index, the client compares its runtime platform — os, architecture, and where relevant `variant` (arm/v7, arm64/v8) — with each entry. 4. It fetches only the matching manifest, then only that variant's config and layers. So a multi-arch tag costs nothing extra in bandwidth: you never download the other architectures' layers. ## Digests and indexes The index itself has a digest. When you pin `alpine@sha256:…` from a multi-arch tag, you are usually pinning the *index* digest, and the same pin still resolves correctly on every architecture — the platform selection happens after the digest lookup. That is the reference you normally want. Pinning a per-platform manifest digest instead locks the deployment to one architecture, which is occasionally deliberate and usually a mistake in a mixed fleet. This also explains why the same repo digest can accompany different local image IDs on different machines: the index digest is shared, the resolved config differs. ## Building multi-arch images `docker buildx` is the standard route. `docker buildx build --platform linux/amd64,linux/arm64 -t repo/app:1.0 --push .` builds each platform (natively on multi-node builders, or via QEMU emulation on one host) and pushes an index that ties the results together. `docker buildx imagetools create` can also assemble an index from manifests that were built and pushed separately, which suits pipelines where each architecture builds on its own native runner. Build-tool-driven image building has its own conventions; the registry-side artifact is the same index either way. ## Inspecting `docker buildx imagetools inspect repo/app:1.0` lists the platforms in the index; adding `--raw` prints the index JSON exactly as stored. `docker manifest inspect` does the same for older workflows. If the output shows a single manifest with a `config` and `layers` section rather than a `manifests` array, the tag is single-platform. ## Failure modes worth naming - **`exec format error` on start**: a single-arch image landed on a different architecture. The image is fine; the reference simply had no variant for that platform. - **Silent emulation**: with binfmt_misc/QEMU registered, a foreign-arch image runs, but far slower and sometimes with subtle syscall differences. Teams often discover this as a mysterious CPU regression. - **`--platform` on run versus build**: `docker run --platform` selects from an index (or forces emulation); `docker build --platform` sets what you produce. Confusing the two produces images that are correct locally and broken in the cluster. - **Attestations in the index**: buildx may add provenance/SBOM entries with `platform.architecture: unknown`. Tools that assume every index entry is a runnable image mis-report them.
- You pinned a digest taken from a multi-arch tag, and now arm64 nodes fail to pull it. What likely happened?The pinned digest is almost certainly a per-platform manifest digest rather than the index digest — for example copied from an amd64 machine's inspect output of the resolved image. A manifest digest names one architecture only, so other platforms have nothing to resolve. Re-pin using the index digest, which `docker buildx imagetools inspect` reports at the top.
- Does a multi-arch tag make pulls larger or slower?No. The index is a few kilobytes and the client downloads only the matching platform's manifest, config and layers. The extra cost is entirely on the build side, where each platform must actually be built, natively or under emulation.
saying these in an interview costs you the question
- Believing one image contains binaries for all architectures and the runtime picks at exec time
- Thinking the client downloads every platform's layers and discards the unused ones
- Assuming `docker build --platform` on a single host produces a multi-arch tag without buildx and a push of an index
- Treating `exec format error` as a corrupt image rather than an architecture mismatch