Why might an image built on one machine not run on another?

covers: lo-1

Answer

Because an image contains compiled binaries, and a binary compiled for one instruction set is not executable on another. A container shares the host kernel and runs the host’s processor architecture.

Machine Architecture

school and office PCs, most CI runners

linux/amd64

Mac with an M-series chip

linux/arm64

Raspberry Pi 4 and 5

linux/arm64

newer cloud instances

linux/arm64, increasingly

This is not an exotic topic — it is why one team’s image runs on three laptops and fails on the fourth. The image built on somebody’s MacBook may not start on the school server, and the pipeline’s image may not start on their MacBook.

Points the answer must contain:

  • Images contain compiled binaries tied to an instruction set

  • amd64 and arm64 are the two that matter in this class

  • A container runs the host architecture, unlike a VM with its own kernel

  • Mixed hardware in one team makes this an everyday problem

How do you recognise an architecture mismatch?

covers: lo-2

Answer

By the error, which does not say "wrong architecture":

exec /app/entrypoint.sh: exec format error

exec format error means the kernel was asked to run a binary it does not understand — the same error a Linux kernel gives for a Windows executable, for the same reason. The commonest wrong response is to start rewriting the entrypoint script.

Inspect instead of guessing:

docker image inspect nginx:1.27 --format '{{.Os}}/{{.Architecture}}'
docker buildx imagetools inspect nginx:1.27
uname -m

On macOS and Windows the picture is muddier: Docker emulates the foreign architecture with QEMU, so the image runs — five to ten times slower, and occasionally with subtle failures in native libraries. "It works but it is inexplicably slow" is a symptom of this, not of the code.

Points the answer must contain:

  • exec format error, and the platform-mismatch warning on a Mac

  • It is a binary-format problem, not a broken script

  • docker image inspect / buildx imagetools inspect / uname -m

  • Emulation hides it and shows up as unexplained slowness

How do you build and publish a multi-architecture image?

covers: lo-3

Answer
docker buildx create --name multi --use --bootstrap
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t ghcr.io/htl-leonding-example/booking:0.3.0 \
  --push .

Two details that trip up the first attempt.

--push, not --load. The local image store holds one architecture, so a multi-platform build cannot be loaded into it. It goes straight to a registry, or you build one platform at a time with --load for local testing.

It is slower. Each platform is built separately and the non-native one is emulated, so two platforms cost more than twice one — which is the argument for building them in the pipeline rather than on a laptop.

For a JVM application there is a shortcut: the .jar is architecture-independent, so only the base image differs, and a multi-architecture base such as eclipse-temurin:25-jre-alpine gives both variants from the same Dockerfile. With native code — node-gyp, Python C extensions, Go with cgo — the build genuinely happens twice.

Points the answer must contain:

  • buildx with --platform listing the platforms

  • --push is required; --load cannot hold two architectures

  • Slower than a single build; belongs in the pipeline

  • JVM artefacts are portable, native code is not

What is an image index and how is the right variant chosen?

covers: lo-4

Answer

A tag in a registry does not have to point at one image. It can point at an image index — a manifest list — a small document naming one image per platform.

q image index

docker pull booking:0.3.0 fetches the index, the client looks up its own platform and pulls that digest. Nobody has to know which machine they are on, which is why nginx:1.27 simply works everywhere.

One consequence worth remembering: the index has its own digest and each variant has another. booking@sha256:aaa… pins one architecture — so pinning by digest, which is otherwise good practice, silently makes an image single-platform for everybody who uses it.

Points the answer must contain:

  • A tag may point at an index listing one image per platform

  • The client selects by its own platform automatically

  • Index and variants have separate digests

  • Pinning a variant digest pins the architecture too

When is a multi-architecture build worth its cost?

covers: lo-5

Answer
Build both Do not bother

the team has Intel and Apple Silicon machines

everything, server included, is amd64

it will run on a Pi or an ARM cloud instance

the image is only used by the pipeline

other people pull the image

it is a throwaway build for one experiment

it is a base image others build on

the build takes 20 minutes and nobody needs the second variant

The honest default for a school project: if the team is mixed, build both; if not, build one and write down which. The second half is what gets skipped, and it is the difference between a two-minute diagnosis and a lost afternoon.

Useful in either case: --platform linux/amd64 on docker run forces a variant. On a Mac that reproduces what the server will do — slowly, under emulation, but faithfully.

Points the answer must contain:

  • Worth it for mixed teams, ARM targets and images others consume

  • Not worth it for single-architecture environments and throwaway builds

  • Either way, document which platforms the image supports

  • --platform on run forces a variant for reproducing another machine

Why can buildx build --platform amd64,arm64 --load never work?

covers: lo-3, lo-4

Answer

Because --load writes into the local image store, and a local image under a tag is one image for one architecture. There is nowhere for the second variant to go: the store has no concept of an index sitting under a local tag.

The multi-platform result is an image index pointing at two images, and that structure only exists in a registry. Hence --push, which is not a convenience but the only destination that can represent the result.

The two ways forward, depending on what you actually want:

  • to publish: --push to a registry, and the index is created for you;

  • to test locally: build one platform at a time with --platform linux/arm64 --load, which produces an ordinary single image you can run.

The error message on the first attempt is unhelpfully terse, which is why this is worth knowing before you meet it rather than after twenty minutes of retrying.

Points the answer must contain:

  • --load targets the local store, which holds one image per tag

  • The multi-platform result is an index, which only exists in a registry

  • Use --push to publish

  • Build a single platform with --load for local testing