Chapter 12Lesson 04~125 minutes

Multi-Platform Images, QEMU Emulation, Native Builders, Manifest Lists, and Cross-Architecture Delivery: Diagnostics, Failure Modes, Security, and Performance

Cross-architecture failures are easy to misclassify: exec-format errors, unexpectedly slow emulated compilation, wrong builder selection, missing platform variants, index/manifest confusion, and local-store limitations can all look like “Docker failed.” This lesson preserves platform evidence first and changes only the layer that actually owns the failure.

DiagnosticsExec formatSlow emulationIndex integrityFirst-failure evidence

Learning objectives

  • Preserve builder, worker, platform, index, manifest, and build-progress evidence before changing emulation, image-store, or registry settings.
  • Diagnose exec-format failures, hidden QEMU use, slow emulated compilation, incorrect TARGET* handling, missing variants, and incompatible local outputs.
  • Explain why uname output alone cannot prove what target artifact the builder intended to produce.
  • Avoid broad daemon changes, privileged binfmt registration, cache deletion, or republishing an index as first-line troubleshooting.
  • Repair the smallest causal layer and then re-verify both index structure and affected platform variant.
Chapter 12 evidence baseline — verified 2026-09-21. Mandatory exercises use only synthetic source, chapter-owned isolated Buildx builders, public base images, and local OCI output. At verification time Docker Engine 29.8.1, Buildx 0.37.1, BuildKit 0.33.0, and Dockerfile frontend 1.27.0 are the current course baselines, but every lab records actual versions, builder driver, worker platforms, and image-store behavior. Fresh Engine 29 installations and Docker Desktop use the containerd image store by default and support multi-platform images locally; upgraded Engine hosts can retain the classic store. The mandatory path uses cross-compilation-style BUILDPLATFORM/TARGET* separation and OCI export, so no public registry, cloud account, production daemon, host-wide emulator registration, or real credential is required.

1. Diagnostic rule: platform evidence before remediation

When a multi-platform build fails, preserve the build command, selected builder, buildx inspect, plain progress trace, requested platforms, Dockerfile frontend, source revision, and any index/manifest digests already produced. Do not start by deleting builder cache, changing daemon storage, installing emulators, or republishing tags. Those actions can erase the evidence that tells you which layer failed.

2. Evidence-first failure sequence

  1. Confirm host/daemon/context and actual Engine/Buildx versions.
  2. Confirm selected builder, driver, BuildKit version, nodes, and advertised platforms.
  3. Confirm requested --platform set and automatic TARGET* values.
  4. Find the first failing build vertex in plain progress.
  5. Classify whether it required target-platform execution.
  6. Inspect exporter/index state separately from local image-store state.
  7. If published, resolve the exact index and child manifest digests.
  8. Apply one bounded correction and rerun the smallest target/platform scope.

3. Failure mode: exec format error

An “exec format” error usually means a process binary does not match an executable platform available to the kernel/emulator path. It may occur during build or runtime. First compare the failing stage’s effective platform with the binary it is trying to execute. If the stage defaulted to the target platform, BuildKit may be trying to run arm64 user space on an amd64-only worker without emulation.

Do not fix this by blindly forcing the entire build to amd64; that can silently produce the wrong artifact. Decide whether the step should run on BUILDPLATFORM and cross-compile, or whether the builder legitimately needs arm64 execution through native/emulated capability.

4. Failure mode: the build “works” but arm64 compilation is extremely slow

Capture per-vertex timing from --progress=plain. If compile-heavy RUN steps execute in a target-platform stage on an amd64 workstation, QEMU is a likely performance cause. Compare with a build stage pinned to BUILDPLATFORM and a cross-compiling toolchain, or with a native arm64 node. Do not attribute every timing difference to QEMU; network downloads, cold caches, CPU limits, and different dependencies can also dominate.

5. Failure mode: using uname -m as the only target proof

uname -m reports what the executing kernel environment presents to the running process. In an emulated environment it can be useful runtime evidence; in a cross-compiling build stage it says nothing about the architecture of the artifact produced for another target. Pair runtime architecture with TARGETARCH, binary-format inspection, OCI platform descriptor, and target tests.

6. Failure mode: expecting every multi-platform output to appear in the local image list

Build output is exporter-specific. A docker-container builder does not automatically place results in the Engine image store. OCI output creates a file/layout. Registry output pushes content. Local loading depends on exporter choice and whether the image store supports the result. Fresh Engine 29 and Docker Desktop containerd stores support multi-platform images, but upgraded/classic stores can differ.

Evidence chain: inspect the build command → identify exporter → inspect its destination → only then ask what local image state should exist. “Build succeeded but docker image ls shows nothing” can be correct behavior.

7. Failure mode: index exists, one variant is broken

A top-level index can be structurally valid while one platform image is unusable. Resolve every descriptor and maintain separate test evidence. If arm64 fails, record the arm64 manifest digest and test result; do not republish the same tag after rebuilding one child without capturing the new top-level index digest. Any child descriptor change changes the index content and therefore its digest.

8. Failure mode: the wrong builder is selected

Buildx supports multiple builders with different drivers, workers, caches, and platform capabilities. A workstation can switch builders implicitly through previous commands. Always record docker buildx ls and explicitly pass --builder in reproducible automation. If a build suddenly loses arm64 support, confirm builder selection before changing emulators or Dockerfiles.

9. Security-sensitive remediation boundaries

Host-level emulator registration, remote builder administration, daemon image-store migration, and registry publication can be disruptive or privileged. Keep them out of first-line troubleshooting. Use an authorized disposable VM or managed Desktop environment when experimentation requires those capabilities. Remote native builder nodes should use narrow credentials and should not accept untrusted build input with release secrets.

Never turn a platform problem into a broad isolation exception. The fact that a build cannot execute a foreign binary is not evidence that seccomp, firewall, TLS, or other host protections should be weakened.

10. Intentionally broken example: target-stage execution without capability

# syntax=docker/dockerfile:1
FROM alpine:3.22 AS build
ARG TARGETARCH
RUN echo "target=$TARGETARCH" > /target.txt
FROM scratch
COPY --from=build /target.txt /target.txt

With --platform linux/amd64,linux/arm64, the build stage defaults to each target platform. The RUN therefore requires target-platform execution. On a builder without arm64 native/emulation support, arm64 can fail even though the step merely echoes text.

The bounded correction is:

FROM --platform=$BUILDPLATFORM alpine:3.22 AS build
ARG BUILDPLATFORM
ARG TARGETPLATFORM
ARG TARGETARCH
RUN printf 'build=%s target=%s arch=%s\n' \
    "$BUILDPLATFORM" "$TARGETPLATFORM" "$TARGETARCH" > /target.txt

Now the RUN executes on the build platform while producing target-labeled data. This is not a universal fix: if the final artifact genuinely requires target binaries or target-specific package-manager scripts, choose a suitable native/emulated/cross-compilation strategy instead.

11. Performance diagnosis without sacrificing correctness

Measure build duration per platform, per expensive vertex, cache hit rate, network transfer, and CPU utilization. Compare strategy changes with the same source, dependency state, builder resources, and cache conditions. A faster build that skips target tests or emits the wrong architecture is not an optimization.

12. Recovery and smallest safe rerun

If only arm64 failed, rerun only arm64 while diagnosing. Preserve the successful amd64 digest and logs. After the fix, run the full two-platform build to produce a new coherent index and then rerun per-platform verification. This separates local diagnosis from final release creation.

Next lesson

Next: Checkpoint Lab

Produce a full two-platform identity dossier and explicitly classify native, emulated, cross-compiled, and unobserved execution.

Knowledge check

An arm64 target fails with exec-format error. What should you inspect first?

Why can a successful index still contain a broken platform?

Why is docker image ls insufficient after an OCI export?

What does a drastic arm64 compile slowdown suggest?

Why rerun the complete platform set after fixing one child?

Official references and version notes

Version and compatibility note

Version-sensitive statements were rechecked against primary documentation on 2026-09-21. The course baseline is Docker Engine 29.8.1, Buildx 0.37.1, BuildKit 0.33.0, and Dockerfile frontend 1.27.0, but every executable lab records the versions and image-store/builder behavior actually present. Fresh Engine 29 installations and Docker Desktop use the containerd image store by default; upgraded Engine hosts may retain the classic store. Mandatory exercises therefore use an isolated docker-container builder and OCI export so learning does not depend on a registry or on local multi-platform load support.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.