Skip to content
ohsudo, Home
Docker & Containers

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.

Intermediate4 min readApplies to Docker 20.10+ · Docker Buildx · Kubernetes · Apple silicon · AWS Graviton · Raspberry Pi

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 error

On 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: arm64 on amd64, or the reverse. It’s very common with Apple silicon Macs, Graviton and Raspberry Pi.
  • A script without #! on its first line.
  1. The imagebuilt for arm64 (an Apple silicon Mac)
  2. The serveramd64
  3. exec format error(breaks here)
The kernel doesn't recognize the file as something it can run: a binary built for another CPU, or a script with no #! line.

TL;DR

Terminal window
docker image inspect myapp --format '{{.Os}}/{{.Architecture}}' # the image
uname -m # the machine

If they differ, build for the right platform:

Terminal window
docker buildx build --platform linux/amd64,linux/arm64 -t registry.example.com/myapp:1.0 --push .

1. Compare the two architectures

Terminal window
docker image inspect myapp --format '{{.Os}}/{{.Architecture}}'
uname -m

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:

Terminal window
docker buildx imagetools inspect nginx:1.27

Docker 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 requested

The 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:

Terminal window
docker build --platform linux/amd64 -t myapp .

Or set it in docker-compose.yml:

services:
app:
build: .
platform: linux/amd64

For both, with one tag, so that each machine pulls what it needs:

Terminal window
docker buildx create --use --name multi
docker 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:

Terminal window
docker run --privileged --rm tonistiigi/binfmt --install all

In 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 for
RUN curl -fsSL -o /usr/local/bin/tool https://example.com/tool-linux-amd64

Buildx gives you a variable for this:

ARG TARGETARCH
RUN curl -fsSL -o /usr/local/bin/tool "https://example.com/tool-linux-${TARGETARCH}" \
&& chmod +x /usr/local/bin/tool

Check the file inside the image:

Terminal window
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 build
ARG TARGETOS TARGETARCH
WORKDIR /src
COPY . .
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 error

The 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/sh
exec /app/server "$@"

Check the first bytes:

Terminal window
head -c 20 start.sh | od -c | head -n 2

A 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 directory

A file with CRLF endings makes the first line #!/bin/sh\r, and no interpreter has that name. Fix the file, and stop it returning:

Terminal window
sed -i 's/\r$//' start.sh
echo '*.sh text eol=lf' >> .gitattributes

The 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.

Open the terminal →

Found this useful? Share it.

Found a mistake? Let us know at contact@ohsudo.com.

Tools

Try it yourself

Articles

Related articles