Docker · beginner
Docker exec format error: compare image architecture and runtime
Diagnose Docker "exec format error" by comparing image architecture against the host runtime. Distinguish ELF/architecture mismatches from shebang and binary-format failures using inspect, uname, and platform metadata before considering rebuilds or emulation.
The symptoms
- •docker exec or docker run fails immediately with "exec format error" before the process produces any output, logs, or PID activity.
- •docker run starts, then exits within milliseconds with status 126 or 127 and a message naming the entrypoint script or binary.
- •docker compose up reports the container as exited with the same "exec format error" string in service-level logs and never reaches readiness probes.
- •docker build succeeds locally on an arm64 Apple Silicon or Graviton host, then fails identically when pulled and run on an x86_64 production node.
- •docker pull from a multi-arch manifest succeeds, but a specific digest or tag reports "exec format error" while other tags from the same repository run normally.
Likely causes
- •Image was built for a different CPU architecture (arm64 vs amd64, armv7 vs arm64) than the Docker daemon's runtime, with no fallback to QEMU binfmt emulation.
- •binfmt_misc registration for the foreign architecture is absent or broken, so the kernel refuses to load the ELF interpreter referenced by the image's entrypoint.
- •Image manifest lists a platform not actually present in the repository, causing the daemon to select a binary incompatible with the host.
- •Entry point is a shell script with a Windows-style CRLF line ending or a missing shebang, which the kernel reports as an unrecognised binary format.
- •Image was assembled with mismatched base layers (e.g., a Windows nanoserver layer combined with a Linux binary) producing a non-executable payload.
- •Container was launched with --platform overridden to a value the local daemon cannot satisfy, falling back to a wrong-architecture image variant.
First ten minutes
- 01Capture the exact error string, container ID, image reference, and exit code from docker ps -a or docker compose logs; record whether docker run reached a process at all.
- 02Record the daemon's host architecture with uname -m on the Docker host (not inside any container) and compare against the image's declared Architecture field.
- 03Run docker inspect --format '{{.Architecture}} {{.Os}}' on the resolved image digest to determine what the daemon actually pulled for this platform.
- 04List available binfmt registrations on the host to confirm whether foreign-architecture ELF interpreters are registered and not flagged "disabled".
- 05Pull the same image reference on a known-matching host to separate a registry-side mismatch from a local runtime problem.
- 06If the entrypoint is a script, retrieve it from the image and check the first two bytes and shebang line for format problems before assuming an architecture mismatch.
Evidence to collect
- •Exact error message text, container exit code, and the image reference (including tag and resolved digest) from the failing invocation.
- •Host CPU architecture (uname -m) and operating system of the Docker daemon, with kernel version for binfmt compatibility context.
- •Image manifest entry for the host's OS/arch: Architecture, Os, Variant, and Size fields from docker manifest inspect or registry API metadata.
- •binfmt_misc flags and interpreter paths for any foreign architecture listed in /proc/sys/fs/binfmt_misc/, particularly the "flags" column containing "F" or "disabled".
- •First 16 bytes of the entrypoint binary or script inside the image, sufficient to identify ELF header magic, shebang "#!", or unexpected BOM bytes.
- •Whether docker buildx build --platform was used, and whether --load was specified, since loaded images inherit only the build host's platform unless explicitly added.
Where to look
- •Boundary between the Docker daemon's host kernel and the image's entrypoint binary, where the exec syscall either dispatches to a registered interpreter or returns ENOEXEC.
- •The image manifest in the registry, specifically the manifests list filtered by the daemon's reported OS and architecture, since the daemon's platform selection happens here.
- •The binfmt_misc filesystem (/proc/sys/fs/binfmt_misc/) on the Docker host, which governs whether non-native ELF binaries receive an interpreter at all.
- •The image's OCI config JSON, where Architecture and Os fields declare the intended target; mismatches with the host are the most common root cause.
- •Docker buildx metadata, since a build invoked with --platform linux/amd64 but --load on an arm64 host produces a single-platform image that fails on amd64 nodes.
- •The entrypoint file itself inside the image filesystem, because a corrupted or mis-encoded script produces an identical "exec format error" surface symptom.
Diagnostic steps
- 01Compare uname -m on the Docker host against docker inspect --format '{{.Architecture}}' on the resolved image; equal values rule out architecture mismatch and redirect attention to shebang or binary format.
- 02If architectures differ, list /proc/sys/fs/binfmt_misc/ and verify a registration exists for the image's architecture with flags that do not contain the literal token "disabled".
- 03If binfmt registration is missing or disabled, determine whether the host intentionally restricts QEMU via a configuration file or container runtime policy before assuming a misconfiguration.
- 04Run docker manifest inspect <image> and read the manifests array; confirm a child manifest exists whose Architecture and Os match the host, since the daemon will not silently substitute a missing variant.
- 05Extract the entrypoint file with docker create and docker cp into a temporary directory, then inspect the first bytes: ELF magic 7f 45 4c 46 indicates a binary; "#!" indicates a script; anything else suggests corruption.
- 06For scripts, verify the shebang interpreter exists inside the image with docker run --rm --entrypoint ls <image> /usr/bin/env or equivalent, because a missing interpreter also surfaces as exec failure though not strictly "format error".
- 07Cross-check with docker buildx imagetools inspect <image> to see which platforms the registry advertises; absence of the host's platform confirms a registry-side gap rather than a daemon bug.
Common mistakes
- •Assuming the failure is a permissions problem and chmod-ing the entrypoint, when the kernel is rejecting the binary format before permission checks complete.
- •Rebuilding the image for the host architecture without verifying that the underlying base image actually publishes a manifest entry for that platform.
- •Trusting docker pull's success as evidence of compatibility, since a manifest list pull can resolve to a non-native child without warning.
- •Enabling binfmt on the host without confirming the runtime supports it, because some sandboxed or rootless Docker configurations deliberately disable foreign-arch execution.
- •Tagging an arm64 image as "latest" for an amd64 fleet and relying on auto-selection, when the registry has no amd64 child manifest and the daemon picks the only available variant.
- •Chasing a libc or glibc mismatch when the error message lacks any "version" or "GLIBC" string, since a true format error is reported before dynamic linking is attempted.
Safe fixes
- •If the manifest lacks the host's platform, rebuild with docker buildx build --platform linux/<host-arch> --push and re-pull, then re-run the failing command to confirm the entrypoint starts.
- •If binfmt_misc shows the foreign interpreter as "disabled", enable it with the host's documented binfmt registration mechanism and re-run, verifying the flags column no longer contains "disabled".
- •If the entrypoint is a script with CRLF endings or a missing shebang, correct the source file, rebuild the image, and re-run, then verify the container's exit code is 0 rather than 126/127.
- •If docker buildx --load was used cross-platform, rebuild without --load and push to a registry, then pull per-platform on each target host to avoid silent single-platform loading.
- •If a multi-arch base image is missing the host's variant, switch to a base image that explicitly publishes that architecture in its manifest list, then rebuild and re-verify with docker inspect.
- •If the daemon is rootless or restricted, confirm the runtime policy permits binfmt interpretation before installing QEMU, since some configurations reject foreign-arch execution by design.
Prove the fix
- 01docker run --rm <image> /bin/sh -c 'exit 0' returns exit code 0 with no "exec format error" in stdout or stderr, demonstrating the entrypoint is now executable.
- 02docker inspect --format '{{.Architecture}}' on the running image matches uname -m on the host, confirming the daemon selected a compatible manifest child.
- 03A repeat docker exec into the same container ID succeeds and produces a shell prompt or command output, proving the process boundary is functional.
- 04/proc/sys/fs/binfmt_misc/ shows the relevant interpreter with flags that do not contain "disabled", confirming binfmt will not reject the binary on subsequent runs.
- 05docker compose up brings the service to a healthy state and readiness probes pass, with the prior exec error absent from service logs over a full restart cycle.
Prevention and next steps
- •Pin images by digest and audit the manifest list with docker manifest inspect in CI to fail builds that lack the target architecture before deployment.
- •Build with docker buildx --platform covering every production architecture and push, never --load, so each host pulls its native variant from the registry.
- •Add a pre-deploy smoke test that runs the image's entrypoint on each host class and asserts a zero exit code, catching regressions before traffic is routed.
- •Document the host's binfmt policy in the runtime configuration so engineers know whether foreign-arch images are supported by design or must be rebuilt.
- •Use base images from publishers that explicitly publish multi-arch manifests for all supported platforms, and verify with imagetools inspect during image promotion.
Safe commands and checks
uname -m
docker inspect --format '{{.Architecture}} {{.Os}} {{.Variant}}' <image>
docker manifest inspect <image>
docker buildx imagetools inspect <image>
ls /proc/sys/fs/binfmt_misc/
cat /proc/sys/fs/binfmt_misc/qemu-<arch>
docker create --name <temp-container> <image>
docker cp <temp-container>:<entrypoint-path> <local-path>
docker rm <temp-container>
docker run --rm <image> /bin/sh -c 'exit 0'