A Docker container exits immediately with `exec format error` on an arm64 host but runs on amd64. How do you diagnose and fix it?
answer
- The kernel refused to execute the file
- Compare three things: image, tag, host
- One cause is not about architecture at all
- Emulation is a workaround, not a fix
- Timeouts tuned natively no longer hold
basics
~20 sThe kernel could not execute the binary because it was compiled for another architecture and no emulation handler is registered. Check the image's recorded architecture, rebuild the image for the host's platform, or run it with docker run --platform and accept the emulation cost.
solid answer
~40 s`exec format error` comes from the kernel refusing to execute a file whose format it does not recognise — almost always an amd64 binary landing on an arm64 host with no binfmt handler registered. Confirm it in three moves: `docker image inspect <img> --format '{{.Os}}/{{.Architecture}}'` for what you actually have locally, `docker buildx imagetools inspect <tag>` for what the tag offers in the registry, and `uname -m` or `docker version` for the host. If the tag only offers amd64, the real fix is a multi-platform build. If you must run it now, `docker run --platform linux/amd64` works only where emulation handlers are installed, and the container will be markedly slower. One non-architecture cause is worth remembering: an exec-form entrypoint pointing at a script with no `#!` line produces the same error.
code
bash · 3 linesdocker image inspect telemetry-gateway:4.2 --format '{{.Os}}/{{.Architecture}}'
docker buildx imagetools inspect registry.example.com/telemetry-gateway:4.2
docker version --format '{{.Server.Arch}}'go deeper
Recognise the message as an architecture mismatch and know the one command that confirms it: docker image inspect with the Architecture field, compared against the machine you are on.
Explain why a pull can succeed and the container still fail, and distinguish the architecture cause from an exec-form entrypoint whose script has no interpreter line.
Show a full diagnosis path across image, registry tag and host, and demonstrate that you know emulation shifts real timings — shutdown grace periods, health-check start periods and request deadlines all need revisiting when a workload runs emulated.
Own the guardrail rather than the incident: a release gate that asserts every deployment architecture is present in the published tag, and a stated position on whether emulated execution is ever acceptable in production.
## Where the message comes from `exec format error` is the kernel's `ENOEXEC`, surfaced by the container runtime when it tries to `execve` your entrypoint. The kernel looked at the file, did not recognise it as something it can run, and refused. On containers there are two realistic causes, and separating them is the first move. **Cause one, the common one: wrong architecture.** The binary is a valid ELF executable, but built for a machine type this kernel cannot run — typically an `x86-64` image started on an `aarch64` host — and no `binfmt_misc` handler is registered to hand it to an emulator. **Cause two, the sneaky one: not an executable at all.** An exec-form `ENTRYPOINT ["/entrypoint.sh"]` runs the file directly, with no shell involved. If that script has no `#!` interpreter line, the kernel has nothing to interpret it with and returns exactly the same error, on any architecture. Worth ruling out early, because it looks identical in the logs. ## Diagnosing the architecture case Work outward from the container: ```bash # What did I actually pull? docker image inspect telemetry-gateway:4.2 --format '{{.Os}}/{{.Architecture}}' # What does the tag offer in the registry? docker buildx imagetools inspect registry.example.com/telemetry-gateway:4.2 # What is this host? uname -m docker version --format '{{.Server.Arch}}' ``` Three outcomes follow. If the tag offers only `linux/amd64` and the host is arm64, the image was never built for this hardware — the pull succeeded because a single-platform image is served to anyone who asks. If the tag offers both but the local image says `amd64`, someone pulled or ran with an explicit `--platform linux/amd64`, or an older cached image is being reused. If the image says `arm64` and it still fails, you are in cause two, or a cross-compiled binary was produced for the wrong architecture and copied into a correct-architecture base image — a classic mistake when a build stage is pinned to the build platform but the cross-compilation flags were never wired to `TARGETARCH`. A related error is worth distinguishing: if a tag offers several architectures but not the host's, the *pull itself* fails with a no-matching-manifest error. You never get as far as `exec format error`. Failing at pull means the tag knows about architectures and yours is not among them; failing at exec means an image did land locally and its contents are wrong for this kernel. ## The fixes, in order of preference 1. **Build the image for the architecture.** `docker buildx build --platform linux/amd64,linux/arm64 -t ... --push .` and the host pulls what it needs. This is the only fix that ends the problem. 2. **Run it emulated, deliberately.** `docker run --platform linux/amd64 telemetry-gateway:4.2` selects the amd64 variant. It works only where the host has emulation handlers registered in `binfmt_misc` — Docker Desktop sets those up for you, a plain Linux engine generally does not, which is why the same command works on a laptop and fails on a server. 3. **Run a different image.** For a sidecar or a debugging tool, the fastest fix is often an image that already publishes your architecture. ## The cost of choosing emulation Emulation is a correctness escape hatch, not a deployment strategy, and the numbers are brutal for anything CPU-bound. A telemetry ingest gateway run emulated on an arm64 host will show it everywhere: p99 ingest latency climbing from around 31 ms native to several hundred milliseconds, CPU per request several times higher, and JIT-compiled or heavily vectorised code punished worst of all because the emulator must translate the hot path instruction by instruction. The part teams miss is that emulation also stretches *timings that were tuned natively*. If the deployment stops the gateway with a 4-second SIGTERM grace period, chosen because a native flush of the in-memory batch takes well under a second, the emulated process can miss that window, take SIGKILL, and drop in-flight telemetry on every restart. The container is not crashing and the logs show a clean shutdown starting — it simply never finishes. The same class of problem hits health-check start periods, liveness timeouts and connection deadlines. When you knowingly run emulated, revisit every timeout that was calibrated on native hardware. ## Preventing the recurrence Make the tag multi-platform and verify it in CI with `docker buildx imagetools inspect`, so a silently single-architecture release is caught before deployment rather than by a container that will not start on the one arm64 node in the pool.
- The pull failed with a no-matching-manifest error instead. How is that different?That failure happens before anything runs: the tag advertises several architectures and none of them matches the host, so the client refuses to pull. `exec format error` means an image did land locally — usually a single-architecture image that the registry serves to every client regardless — and only failed when the kernel tried to execute its entrypoint.
- `docker run --platform linux/amd64` works on your laptop but fails on a CI server. Why?Because the flag only selects which variant to run; actually executing a foreign binary needs emulation handlers registered with the kernel's binfmt_misc. Docker Desktop installs those automatically, while a plain Linux engine does not until someone registers them, usually via a privileged helper container at boot.
- How would you stop this failure from reaching production again?Make the release build multi-platform and assert on it: after the push, run `docker buildx imagetools inspect` in the pipeline and fail the job unless every architecture in the deployment target is listed. Then run at least a smoke test on a native runner of each architecture, since an emulated test proves the image starts but not that it performs.
It is a plug that will not fit the socket: the appliance is fine, the country is wrong, and an adapter gets you running but does not make the appliance efficient.
saying these in an interview costs you the question
- Blaming a corrupted image or a bad download
- Assuming a successful pull proves the architecture matches
- Treating docker run --platform as a production fix
- Not knowing emulation needs binfmt handlers on the host
- Overlooking a missing shebang in an exec-form entrypoint
- Expecting emulated performance to be roughly native