Tech Digest

Guide

Why does my Docker container fail with exec format error?

The kernel prints exec format error when it is handed a file it cannot run: a binary for another CPU architecture, or a script whose first bytes are not `#!`. A CRLF line ending or a missing shell gives a different error, `no such file or directory`, and that difference is the quickest diagnosis you have.

Last reviewed

Why does a Docker container fail with exec format error?

Because the kernel was asked to run a file in a format it cannot execute. Usually the image was built for another CPU, such as an arm64 image on an x86 server or the reverse, and the host has no QEMU emulation registered. Otherwise the entrypoint is a script that does not start with #! on its first line. Compare docker image inspect -f '{{.Architecture}}' <image> with uname -m; if they differ, pull the right platform with docker pull <image> or platform: in Compose.

Because the Linux kernel was handed a file it does not know how to run. In practice that is one of two things: an image built for a different CPU, most often an arm64 image on an x86 server or an amd64 image on a Raspberry Pi, or an entrypoint script that does not begin with #!. The execve manual page describes the error as an executable that "is not in a recognized format, is for the wrong architecture, or has some other format error".

Run two commands and you know which case you have:

bash
uname -m                                                  # x86_64 = amd64, aarch64 = arm64
docker image inspect -f '{{.Os}}/{{.Architecture}}' <image>

If they disagree, it is the architecture. If they agree, it is the file the container tried to start.

Two answers circulate, and both are half right#

Most forum threads say exec format error means "wrong architecture" and stop there, which misses the script case, so people rebuild for the right CPU and get the same error back. The second common answer blames Windows line endings. That is the right fix for a different message: a script saved with CRLF endings fails with no such file or directory, not with a format error, because the kernel goes looking for an interpreter called /bin/sh with a carriage return on the end, and there is no such file. The manual lists that case under ENOENT: a script or ELF interpreter that does not exist.

So the two messages split the problem cleanly. exec format error means the file itself is wrong. no such file or directory for a file that plainly exists means the thing it asks for, a shell or a dynamic loader, is missing.

What failed, and how, measured#

We ran each case on Docker Engine Engine 28.1.1 (runc 1.2.5, containerd 1.7.27) on an x86_64 host with no emulators registered, using alpine:3.22 and a five-line test script mounted as the entrypoint.

What the container tried to startMessageExit code
arm64 image, no --platform givenPlatform WARNING, then exec /bin/echo: exec format error255
arm64 image, --platform linux/arm64exec /bin/echo: exec format error, no warning255
arm64 binary, via docker exec into an amd64 containerexec /bb: exec format error255
Script with no #! lineexec /s.sh: exec format error255
Script with a UTF-8 byte-order mark before #!exec format error255
Script with a space before #!exec format error255
Script with CRLF line endingsexec /s.sh: no such file or directory255
#!/bin/bash script in Alpine, which has no bashno such file or directory255
glibc binary copied from Debian into Alpineno such file or directory255
Script without the execute bitexec: "/s.sh": permission denied126
Entrypoint that does not existstat /nope: no such file or directory127

Two details are worth keeping. First, the warning appears only when you did not ask for a platform. With an explicit --platform linux/arm64, Docker assumes you meant it and goes straight to the failure. Second, all the format and loader errors exit 255, while the two failures Docker catches before starting the process get the documented 126 and 127. A container that exits 255 within a second of starting, with nothing in its own log, is worth checking for this first.

The image is single-arch, or your local tag is the wrong one#

A multi-platform image is a list that maps each CPU to its own image, and docker pull picks the entry for the host. When the error happens anyway, one of three things is usually behind it.

The tag has only one architecture. When a tag points to a list that lacks your platform, the pull fails up front:

text
no matching manifest for linux/arm64 in the manifest list entries

When it points to a single image for another CPU, there is no list to check against, so Docker downloads it with nothing but a warning, image with reference ... was found but its platform (linux/amd64) does not match the specified platform (linux/arm64), and the failure waits for the first docker run. That is how a pull can succeed and the container still fail.

Your local tag was replaced by another architecture. This is the one that confuses people most. On the default overlay2 image store, a tag names exactly one image. We pulled alpine:3.22 with --platform linux/arm64 on the x86 host, and from then on a plain docker run alpine:3.22, with no flags at all, ran the arm64 copy and failed. A plain docker pull alpine:3.22 swapped it back.

Compose will not pull it again for you. With the local tag on arm64, docker compose up used it, printed the platform mismatch line among its progress output, and the service exited 255. Compose's default pull_policy is missing, so an image that exists locally is never refreshed. docker compose pull fetched the amd64 image, and the next up recreated the container, which then ran. A platform: linux/arm64 line left in a compose file copied from someone else's Pi setup does the reverse: Compose pulled the arm64 image on purpose and the service failed the same way.

Which images publish which architectures#

We listed the platforms behind twelve common tags with docker buildx imagetools inspect on 22 October 2026. It reads the list without pulling any layers.

Imageamd64arm64arm/v7
emby/embyserver:latestyesyesyes
jellyfin/jellyfin:latestyesyesno
ghcr.io/immich-app/immich-server:releaseyesyesno
vaultwarden/server:latestyesyesyes, plus arm/v6
nextcloud:latestyesyesyes, plus five others
pihole/pihole:latestyesyesyes, plus arm/v6, 386, riscv64
ghcr.io/home-assistant/home-assistant:stableyesyesno
gitlab/gitlab-ce:latestyesyesno
ollama/ollama:latestyesyesno
ghcr.io/paperless-ngx/paperless-ngx:latestyesyesno
mattermost/mattermost-team-edition:latestyesnono
goharbor/harbor-core:v2.13.2yes, single-archnono

Three things follow from it. arm64 is nearly universal: across the 105 tools profiled here, 95.2 percent publish official arm64 builds. arm/v7, which is what a 32-bit Raspberry Pi OS install asks for, is now the gap, and Docker's own install page says Engine 28 is the last major version with packages for 32-bit Raspberry Pi OS. And Emby, whose arm64 image used to live in a separate repository, now publishes all three from the main tag: old guides that say to use emby/embyserver_arm64v8 point you at a single-arch arm64 image, which on an x86 host is a direct route to this error. Choosing home server hardware covers what else changes on ARM, and Jellyfin vs Emby the media server choice.

Mattermost and Harbor are the two in the table with no arm64 build at all, so on a Pi or an ARM VPS the error there is not a configuration mistake you can fix.

Builds fail the same way#

A RUN step in a stage for another architecture fails mid-build:

text
#5 0.357 exec /bin/sh: exec format error
ERROR: failed to solve: process "/bin/sh -c echo built" did not complete successfully: exit code: 255

We got that from both docker build --platform linux/arm64 and a Dockerfile with FROM --platform=linux/arm64. The second also triggered the build check FromPlatformFlagConstDisallowed, since a fixed platform in FROM locks the Dockerfile to one architecture; Docker's advice is to pass --platform on the command line instead. The build left the host's own alpine:3.22 tag alone. docker buildx ls showed what this builder could produce: linux/amd64 (+3), linux/386, and no arm64, which is the sign that no emulator is registered.

Docker Desktop runs and builds other platforms under the QEMU bundled in its VM with no configuration, which is how an image works on a laptop and fails on a Linux server with no emulator. Build with --platform set to the server's architecture before you push, and check the result with docker buildx imagetools inspect.

Fixes, in the order to try them#

1. Pull the right image for this host. docker pull <image> with no flags, or docker compose pull and then docker compose up -d. Remove any platform: line you did not write for this machine. Then re-run the inspect command from the top and check it matches uname -m.

2. Fix the script. Make #!/bin/sh the very first bytes of the file, save it without a byte-order mark, convert CRLF with sed -i 's/\r$//' entrypoint.sh, and use a shell the image has: Alpine has /bin/sh and no /bin/bash unless you install it. file entrypoint.sh on the host reports with BOM and with CRLF line terminators when either is present, and plain ASCII text for a script with no #! line at all.

3. Pick another image or another host. If the tag has no build for your CPU, look for an official one under a different tag before trying anything else. If there is none, run that service on an amd64 machine.

4. Emulate, as a last resort. docker run --privileged --rm tonistiigi/binfmt --install arm64 registers QEMU for arm64 with the host kernel, and running the same image with no arguments lists what is installed. Docker's own documentation warns that emulation can be much slower than native builds, especially for compilation and compression, so treat it as a way to build or test, not to run a database. The registration is made with the host kernel, so it applies to the whole machine, not to one container.

Do not reinstall Docker. In every case we tested, the fix was the image, the tag or the file.

When this answer stops applying#

  • Engine 29 and the containerd image store. Docker's documentation says Engine 29.0 and later use the containerd image store by default, which supports multi-platform images. We tested only the overlay2 store on Engine 28.1.1, where one tag is one image; the stale-tag behaviour may differ on the containerd store.
  • Older runtimes word it differently. Older runc versions printed the same failure as standard_init_linux.go:...: exec user process caused "exec format error". Same cause, same fixes.
  • Docker Desktop, rootless Docker, LXC. We did not test them. If you are choosing where Docker runs on a Proxmox host in the first place, Docker in LXC or a VM on Proxmox covers the trade-off.

What to do next#

Run uname -m and the inspect command on the failing image. If they disagree, docker compose pull and recreate. If they agree, open the entrypoint script and look at its first line. If the container now starts but dies with another code, What Docker exit code 137 means explains the most common one, and if pulls start failing with rate limits after repeated retries, Docker Hub pull limits and update checkers has the numbers.

Questions#

Does exec format error always mean the wrong architecture?

No. It means the kernel did not recognise the file's format. A binary for another CPU is the common case, but a script with no #! line, a byte-order mark before #!, or a space before it gave the same exec /s.sh: exec format error in our tests. A script saved with Windows line endings does not: it gives no such file or directory, because the kernel looks for an interpreter literally named /bin/sh followed by a carriage return.

How do I check which architecture a Docker image is?

For an image you already have, docker image inspect -f '{{.Os}}/{{.Architecture}}{{if .Variant}}/{{.Variant}}{{end}}' <image>. Compare it with uname -m on the host, where x86_64 means amd64 and aarch64 means arm64. For an image you have not pulled, docker buildx imagetools inspect <image> lists every platform the tag publishes without downloading the layers. A tag with one platform and no list behind it is single-architecture.

Why does docker pull work and docker run fail with exec format error?

Because the image you pulled has a single architecture, not a list. When a tag points to a multi-platform list that lacks your platform, the pull itself fails with no matching manifest for linux/arm64 in the manifest list entries. When it points to one image for another CPU, Docker downloads it with only a warning, and the failure appears at the first docker run as exec format error, exit code 255.

Can I run an arm64 image on an x86 server?

Yes, under QEMU emulation, which docker run --privileged --rm tonistiigi/binfmt --install arm64 registers with the kernel. Docker's documentation warns that emulation can be much slower than native, especially for compilation and compression, so it suits builds and occasional tools, not a media server or a database. Docker Desktop ships QEMU already, which is why an image that runs on a laptop can fail on a Linux server.

Why does my Raspberry Pi say no matching manifest for linux/arm/v7?

Because the Pi runs a 32-bit Docker, which asks for ARMv7 images, and many projects now publish only amd64 and arm64. Eight of the twelve images we checked had no arm/v7 build, Jellyfin, Immich and Home Assistant among them. Docker itself says Engine 28 is the last major version with packages for 32-bit Raspberry Pi OS. The fix is a 64-bit OS reinstall, not a different tag.

Sources#

Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.