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

  1. 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.
  2. 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.
  3. 03Run docker inspect --format '{{.Architecture}} {{.Os}}' on the resolved image digest to determine what the daemon actually pulled for this platform.
  4. 04List available binfmt registrations on the host to confirm whether foreign-architecture ELF interpreters are registered and not flagged "disabled".
  5. 05Pull the same image reference on a known-matching host to separate a registry-side mismatch from a local runtime problem.
  6. 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

  1. 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.
  2. 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".
  3. 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.
  4. 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.
  5. 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.
  6. 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".
  7. 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

  1. 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.
  2. 02docker inspect --format '{{.Architecture}}' on the running image matches uname -m on the host, confirming the daemon selected a compatible manifest child.
  3. 03A repeat docker exec into the same container ID succeeds and produces a shell prompt or command output, proving the process boundary is functional.
  4. 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.
  5. 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'