The image's CPU architecture doesn't match the host: amd64 image pulled on an ARM64 machine (or the reverse). Multi-platform builds with buildx are the durable fix; --platform is the quick one.
The image was built/pulled for x86-64 but runs on arm64. Docker pulls the best-matching tag by default — if only amd64 exists, an ARM host still gets it, and the kernel can't execute those binaries. docker image inspect <img> --format '{{.Architecture}}' confirms in seconds.
Scratch-based Go/Rust images that copied an amd64 binary into an otherwise-unspecified image: the manifest says unknown, the binary is amd64, the ARM host refuses. Same error, build-side origin.
docker image inspect <image> --format '{{.Architecture}}' ; uname -m
docker run --platform linux/amd64 <image> # via Rosetta/QEMU emulation on Apple Silicon — slower but runs
docker buildx build --platform linux/amd64,linux/arm64 -t <user>/<image>:tag --push .
# service: platform: linux/amd64 # until multi-arch images exist for that dependency
exec format error at STARTUP (before any app logs) is architecture almost by definition: the kernel literally can't parse the first instruction. App-level errors come after init begins. QEMU emulation (Docker Desktop's Rosetta setting) makes amd64-on-ARM work but costs performance — fine for a dev dependency, wrong answer for a server workload.
Same image bytes, different CPU: the amd64 binaries execute on Intel, fail on ARM. The image needs a matching architecture build — check with docker image inspect before anything else.
Only if the repository publishes multi-arch manifests. For single-arch images, the pull succeeds anyway and the failure happens at run: exec format error is that exact moment.
A production-shaped compose stack: healthchecks, resource limits, log rotation. Never debug a boot race again.
Docker Production Starter — $19 →One-time. Yours to modify. Instant download from the NinjaOps template store.