"exec format error" in Docker: ARM vs x86 Images Explained
exec format error almost always means a container image built for one CPU architecture (ARM, like Apple Silicon Macs) is running on another (x86/amd64 servers), or vice versa. How to check, build for the right platform with --platform and buildx, and the other cause: scripts without a shebang or with Windows line endings.
exec /usr/local/bin/docker-entrypoint.sh: exec format error
standard_init_linux.go: exec user process caused: exec format error
The kernel was asked to run a file it doesn't understand. In Docker, there are two common reasons — and the first one is behind most cases.
Cause 1: wrong CPU architecture (the usual one)
Computers have different processor architectures:
- amd64 (also called x86_64) — most cloud servers and Intel/AMD PCs.
- arm64 (also called aarch64) — Apple Silicon Macs (M1–M4 and later), Raspberry Pi, and ARM cloud servers like AWS Graviton.
A Docker image contains programs compiled for one architecture (unless it's a multi-arch image). Build an image on an M-series Mac and by default it's arm64. Push it to a normal x86 server, run it, and: exec format error.
Check
docker image inspect myapp --format '{{.Architecture}}' # the image
uname -m # the machine
arm64 image on an x86_64 machine (or the reverse) is your answer.
Fix: build for the server's platform
docker build --platform linux/amd64 -t myapp .
In Compose:
services:
app:
build: .
platform: linux/amd64
Building for another architecture uses emulation on your Mac, so it's slower — but it works.
Better: build multi-architecture images
docker buildx can build one image that works on both:
docker buildx build --platform linux/amd64,linux/arm64 -t you/myapp:latest --push .
The registry stores both versions, and each machine pulls the one that matches.
Even better: build on the server or in CI
If images are built by CI (GitHub Actions runners are amd64 by default) or on the server itself, they match the server automatically. (Deploy to a VPS with GitHub Actions)
Base images
Most official images (node, python, postgres) are multi-arch, so they're fine. Problems come from binaries you download or copy in — a CLI tool fetched with curl for the wrong architecture, or a node_modules folder with native modules copied from your Mac into the image. Always run npm ci inside the Dockerfile rather than copying node_modules. (Production Dockerfile for Node)
Cause 2: a script the kernel can't run
If the architecture matches, look at the entrypoint script.
Missing shebang
A script must start with a line saying which interpreter runs it:
#!/bin/sh
Without it, exec doesn't know what to do with the file.
Windows line endings
A script saved on Windows with CRLF line endings makes the shebang #!/bin/sh\r, which doesn't exist. Convert to LF:
sed -i 's/\r$//' docker-entrypoint.sh
…and add a .gitattributes so it doesn't come back. (LF will be replaced by CRLF)
Not executable
Make sure the script has execute permission (this usually gives "permission denied", but check anyway):
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
Quick checklist
uname -mon the server vs the image's architecture.- Rebuild with
--platform linux/amd64(or multi-arch with buildx). - Install dependencies inside the image, don't copy them in.
- Entrypoint script: shebang, LF line endings, executable.
EasySpawn builds your app on the same server it runs on, so architecture mismatches simply can't happen. See how it works or join the waitlist.
Related: Dockerfile Explained · Writing a Production Dockerfile for Node.js · What Is Docker? · Docker Image vs Container
Keep reading
npm run build Fails but npm run dev Works: Why and How to Fix It
Development mode is forgiving; production builds are strict. The common reasons a build fails when dev works — TypeScript and lint errors, case-sensitive imports, missing environment variables, server-only code in the browser, prerendering errors and memory — with the fix for each.
Why Is My Environment Variable Undefined? (Vite, Next.js, Node)
Your .env file has the value but the code sees undefined. The reasons are almost always the same: the wrong prefix (VITE_, NEXT_PUBLIC_), the wrong file name or folder, not restarting the dev server, build-time vs runtime, or the variable never being set in production.