Docker "exec format error": the wrong CPU architecture, or a script with no shebang
Fix Docker "exec /app: exec format error": an image built for another architecture (arm64 vs amd64) or a script with no shebang. Check it, then build with buildx.
by OhSudo ··
Checked on real software: Docker · November 2026How we check
Not sure this is your cause?Find it step by step: Container won't start →
Part of a learning path:Docker: from first container to production
exec /usr/local/bin/app: exec format errorOn Kubernetes, the pod goes to CrashLoopBackOff, with the same line in its logs.
The kernel tried to start that file, and doesn’t recognize its format as something it can run. There are two usual causes, and they’re simple to tell apart:
- A binary built for another CPU architecture:
arm64onamd64, or the reverse. It’s very common with Apple silicon Macs, Graviton and Raspberry Pi. - A script without
#!on its first line.
- The imagebuilt for arm64 (an Apple silicon Mac)runs on
- The serveramd64kernel refuses(fails)
- exec format error(breaks here)
TL;DR
docker image inspect myapp --format '{{.Os}}/{{.Architecture}}' # the imageuname -m # the machineIf they differ, build for the right platform:
docker buildx build --platform linux/amd64,linux/arm64 -t registry.example.com/myapp:1.0 --push .1. Compare the two architectures
uname -m says x86_64 for amd64, and aarch64 for arm64. To check an image in a registry, without pulling it, and see every platform it has:
docker buildx imagetools inspect nginx:1.27Docker usually warns you, if you read the output of docker run:
WARNING: The requested image's platform (linux/arm64) does not match the detected host platform (linux/amd64/v3) and no specific platform was requestedThe classic story: you build on an M-series Mac, which gives you an arm64 image, push it, and it fails on your amd64 server.
2. Build for the right platform
For one platform:
docker build --platform linux/amd64 -t myapp .Or set it in docker-compose.yml:
services: app: build: . platform: linux/amd64For both, with one tag, so that each machine pulls what it needs:
docker buildx create --use --name multidocker buildx build --platform linux/amd64,linux/arm64 -t registry.example.com/myapp:1.0 --push .--push is needed because a multi-platform image usually can’t be loaded into the local image store. Building for another architecture uses emulation, which can be slow. On Linux, you may have to set it up first:
docker run --privileged --rm tonistiigi/binfmt --install allIn GitHub Actions, that’s docker/setup-qemu-action and docker/setup-buildx-action, and then platforms: in docker/build-push-action. The CI/CD Pipeline Generator writes the build job, and you can add platforms: to it.
3. Your Dockerfile downloads the wrong binary
The image’s platform can be right, and the program in it still wrong, since a hard-coded download ignores the platform:
# Always downloads amd64, whatever you build forRUN curl -fsSL -o /usr/local/bin/tool https://example.com/tool-linux-amd64Buildx gives you a variable for this:
ARG TARGETARCHRUN curl -fsSL -o /usr/local/bin/tool "https://example.com/tool-linux-${TARGETARCH}" \ && chmod +x /usr/local/bin/toolCheck the file inside the image:
docker run --rm --entrypoint sh myapp -c 'uname -m; ls -l /usr/local/bin'docker run --rm -v "$PWD":/x alpine sh -c 'apk add -q file && file /x/app'For compiled languages, cross-compile explicitly. In Go:
FROM --platform=$BUILDPLATFORM golang:1.25 AS buildARG TARGETOS TARGETARCHWORKDIR /srcCOPY . .RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /app .A binary copied in from your laptop is the same mistake. And FROM --platform=linux/amd64 on the last stage fixes the platform regardless of what you ask for, so remove it unless you mean it.
4. A script with no shebang
Now the architecture’s right, and it still fails:
exec /app/start.sh: exec format errorThe kernel can’t guess the interpreter of a file without #! at the very start. A shell would quietly run it anyway, which is why it works in your terminal and not as a container’s ENTRYPOINT. Add it as the first line, with no blank line or space before it:
#!/bin/shexec /app/server "$@"Check the first bytes:
head -c 20 start.sh | od -c | head -n 2A file saved with a byte order mark (357 273 277) has the same problem, as #! is no longer at the beginning. Save it as UTF-8 without a BOM.
Another way out is to name the interpreter in the Dockerfile: ENTRYPOINT ["sh", "/app/start.sh"].
5. Windows line endings
Nearly the same situation, with a different message:
exec /app/start.sh: no such file or directoryA file with CRLF endings makes the first line #!/bin/sh\r, and no interpreter has that name. Fix the file, and stop it returning:
sed -i 's/\r$//' start.shecho '*.sh text eol=lf' >> .gitattributesThe same message can mean a binary built for glibc on Alpine: see the Alpine article.
6. Other cases
A corrupted or partial download. A curl that saved an error page, for example, has the wrong format too. Add -f to curl, and check file shows an ELF binary, not HTML document.
On a Kubernetes cluster with mixed nodes, the same image works on some nodes and not others. Build multi-platform, or limit the pod with nodeSelector: { kubernetes.io/arch: amd64 }. If that pod loops, see CrashLoopBackOff.
On the host, outside Docker: -bash: ./app: cannot execute binary file: Exec format error is the same thing: a wrong architecture, or a file that’s not a program for this system.
Warning
Emulation is fine for building, and for testing. But running an amd64 image on arm64 under QEMU in production is much slower, and some programs crash under it. Build native images for what you run.
Shell, we have a problem!
Similar, but different: no matching manifest for linux/arm64/v8 in the manifest list entries means the image has no version for your platform, and fails at docker pull. permission denied on an entrypoint means it’s not executable: chmod +x. And error while loading shared libraries is a missing library. The Dockerfile Generator writes multi-stage builds, so nothing gets copied in from your laptop. On a Mac, outside Docker, the same mismatch is zsh: bad CPU type in executable.
$ ./practice.sh
Practice this in the terminal
Open the Terminal Playground with this article’s scenario loaded. It is safe and runs in your browser.
Found this useful? Share it.
Found a mistake? Let us know at contact@ohsudo.com.