You're on an M1, M2, or M3 Mac. You run docker build on a Dockerfile that pulls something like node:18 or python:3.11, and the build dies almost immediately with:
standard_init_linux.go:228: exec user process caused: exec format error
Or you see it mid-build when a RUN line tries to execute a binary. Sometimes it shows up in docker run instead, right after the container starts. Either way, the container can't execute the binary inside it because the binary is the wrong architecture for the kernel that's trying to run it.
What's actually going on
Apple Silicon is ARM64 (aarch64). Most public Docker images on Docker Hub are built for AMD64 (x86_64) because that's what 99% of servers run. When you pull someimage:latest on your Mac, you usually get the AMD64 variant unless the image publisher explicitly published a multi-arch manifest or an ARM64 tag.
Docker Desktop on Mac runs containers inside a Linux VM. That VM's kernel is ARM64. So when Docker tries to run an x86_64 binary on an ARM64 kernel, the kernel says "I don't know this format" and you get exec format error. It's the Linux equivalent of trying to run a Windows .exe on macOS.
The culprit here is almost always one of three things: a base image with no ARM64 variant, an explicit --platform=linux/amd64 somewhere in your chain, or a binary you downloaded inside the build (like a Go cross-compiled tool or a prebuilt CLI) that's x86_64 even though the rest of the image is fine.
The fix, step by step
- Figure out what architecture you're actually getting. Run this before you do anything else:
docker image inspect yourbaseimage:tag --format '{{.Os}}/{{.Architecture}}'
If it says linux/amd64, that's your problem. If it says linux/arm64, your base image is fine and the issue is a binary you're pulling in at build time.
- Check for a hard-coded platform flag. Search your Dockerfile and any
docker buildwrapper scripts:
grep -rn -- "--platform" Dockerfile* docker-compose*.yml .github/ Makefile 2>/dev/null
If you see FROM --platform=linux/amd64 node:18, that's forcing AMD64 even on ARM. Remove it unless you have a real reason to keep it.
- Pick the right base image. Most official images now ship multi-arch. Instead of pinning a platform, just let Docker pick:
FROM node:20-bookworm-slim
# no --platform flag, Docker picks native arch
If the image you need has no ARM64 variant (some older Java images, a few vendor SDKs, plenty of niche tooling), you have two options: switch to a base that does, or cross-build with Buildx (step 5).
- If you must run AMD64, install QEMU support. Docker Desktop bundles QEMU these days, so this usually just works:
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
Then build with the platform flag and expect a 3–10x slowdown:
docker build --platform linux/amd64 -t myapp:amd64 .
Don't do this for day-to-day dev. It's slow, and any file-mounting tooling gets weird. It's for producing x86 artifacts when you can't build native.
- Use Buildx for real multi-arch output. If your goal is to push an image that runs on both architectures, this is the real fix. Set up a builder once:
docker buildx create --name multiarch --use
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t yourrepo/yourapp:latest \
--push .
You need --push (or --load for single-platform) because the local Docker daemon can't hold a multi-arch manifest. Pushing to a registry is how the manifest gets created.
- Fix the downloaded binary case. If your base image is ARM64 but the build still fails on a
RUN ./some-tool, you're pulling an x86_64 binary at build time. Common offenders: old Terraform releases, some AWS CLI install scripts, a handful of Go CLI tools, and anything shipped as a tarball without an ARM64 build. Check the release page and grab the correctlinux_arm64orlinux_arm64.tar.gzasset. If the project genuinely doesn't ship ARM64, you'll have to cross-build that one binary or wrap it in QEMU.
If it still fails
- Docker Desktop is out of date. Buildx and QEMU support on Apple Silicon has improved a lot. Anything below 4.20 is asking for trouble. Update it.
- You're on an old Dockerfile that pulls
debian:stretchor similar. Old Debian and Ubuntu base images don't have ARM64 variants. Move tobookworm,jammy, oralpine. - Your local
docker-compose.ymlsetsplatform: linux/amd64under a service. This is sneaky and easy to miss because it's not in the Dockerfile. Grep the whole repo. - You're building inside CI but testing locally. Make sure both use the same platform. A build that works in GitHub Actions (which defaults to AMD64 runners) will not necessarily behave the same in your local ARM64 setup, and vice versa.
- Check for a stale builder. Run
docker buildx ls. If you're accidentally still on the default builder,docker buildx use multiarchto switch back.
Don't bother reinstalling Docker or nuking the VM over this. It won't help. exec format error is almost never a Docker Desktop bug — it's an architecture mismatch, every time. Fix the mismatch and the error goes away.