Fix 'exec format error' on AWS Fargate: ARM vs x86 images
Your container runs locally but Fargate kills it on start. Check which CPU architecture the image was built for, then rebuild or change the task platform.
TL;DR#
Fargate stops your container on start with exec user process caused: exec format error when the image's CPU architecture does not match the task's. The classic case: the image was built on an Apple Silicon (M1/M2/M3) Mac, so it is linux/arm64, and the Fargate task runs on the default X86_64. Fix it one of two ways: rebuild the image for linux/amd64 with docker buildx build --platform linux/amd64, or set the task definition's runtimePlatform.cpuArchitecture to ARM64 so the task matches the image.
The error#
The container exits immediately, the ECS service keeps restarting it, and the stopped task's log shows one line, reported verbatim on Stack Overflow by a developer deploying a MERN app to ECS/Fargate:
standard_init_linux.go:219: exec user process caused: exec format errorNewer container runtimes word it differently, naming the file they failed to start, for example exec /usr/local/bin/docker-entrypoint.sh: exec format error. Same failure, same cause. The same image usually runs perfectly on the laptop that built it, which is what makes this error confusing: nothing in your code or your Dockerfile is wrong.
Why it happens#
A container image is not portable machine code. Every binary inside it, from /bin/sh to node to any native module compiled during npm install, is compiled for one CPU architecture. When a container starts, the Linux kernel on the host is asked to execute the image's entrypoint. If that binary was compiled for arm64 and the host CPU is x86_64, the kernel cannot load it and returns ENOEXEC, which the runtime reports as exec format error. The process never starts, so your application logs nothing.
Two defaults collide here:
- Docker builds for the machine it runs on. On an Apple Silicon Mac, a plain
docker buildproduces alinux/arm64image, pulling the arm64 variants of multi-platform base images such asnodeoralpine. - Fargate defaults to x86. In the ECS task definition,
runtimePlatform.cpuArchitecturedefaults toX86_64. The ECS task definition reference listsX86_64andARM64as the valid values, withARM64available for Linux tasks.
The accepted answer on the source question names exactly this mismatch: images built on the M1's ARM architecture cannot run on the x86-64 hosts the task was scheduled on.
Fix 1: rebuild the image for linux/amd64#
This keeps the Fargate task on its default architecture. BuildKit's buildx builds for a target platform other than your own, using QEMU emulation when the host cannot run the target natively (Docker multi-platform builds):
-
Build and tag the image for x86-64:
bashdocker buildx build --platform linux/amd64 -t my-api:1.4.0 . -
Tag and push it to the registry the task pulls from (Amazon ECR here, in
eu-west-2):bashdocker tag my-api:1.4.0 123456789012.dkr.ecr.eu-west-2.amazonaws.com/my-api:1.4.0 docker push 123456789012.dkr.ecr.eu-west-2.amazonaws.com/my-api:1.4.0 -
Register a new task definition revision pointing at the new tag and update the service.
If you prefer the platform to live in the Dockerfile itself, pin it on the base image. Another answer on the question does exactly that, and it is what makes an accidental local docker build safe:
FROM --platform=linux/amd64 node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]Expect the emulated build to be slower than a native one. The Docker documentation warns that QEMU emulation "can be much slower than native builds", especially for compilation-heavy steps such as native npm modules. If npm ci compiles C++ addons, that is where the time goes.
Fix 2: run the Fargate task on ARM64#
If your image is arm64 and you would rather keep it that way, make the task match it. In the task definition, add or change the runtimePlatform block (the other required fields, such as containerDefinitions and "networkMode": "awsvpc", are omitted here and stay as they are):
{
"family": "my-api",
"requiresCompatibilities": ["FARGATE"],
"runtimePlatform": {
"operatingSystemFamily": "LINUX",
"cpuArchitecture": "ARM64"
},
"cpu": "512",
"memory": "1024"
}Every task definition used by one service must share the same cpuArchitecture value, so change it for the whole service, not for a single revision you test by hand. This only works if every base image and every native dependency in the image exists for arm64; most official images (node, alpine, postgres) publish arm64 variants, but a private base image or a vendor binary may not.
Fix 3: publish one image for both architectures#
When the same image must run on arm64 laptops and x86 servers, build a multi-platform image. The registry then stores one manifest list, and each host pulls the variant for its own CPU:
docker buildx build --platform linux/amd64,linux/arm64 -t 123456789012.dkr.ecr.eu-west-2.amazonaws.com/my-api:1.4.0 --push .Push straight to the registry: the documentation notes that images built with the docker-container driver are not loaded into the local image store automatically, so --push is the reliable way to get every variant where the task will pull it. If the builder cannot emulate a platform, register QEMU handlers first with docker run --privileged --rm tonistiigi/binfmt --install all.
Verify the fix#
Check what you built before you deploy it. For a local image:
docker image inspect my-api:1.4.0 --format '{{.Os}}/{{.Architecture}}'It must print linux/amd64 for a default Fargate task, or linux/arm64 if you chose Fix 2. For an image already pushed, list every platform its manifest carries:
docker buildx imagetools inspect 123456789012.dkr.ecr.eu-west-2.amazonaws.com/my-api:1.4.0Then force a new deployment of the service and watch the task reach RUNNING. If the task still stops, open the stopped task's reason in the ECS console: a different message there means you have moved on to a different problem, such as a missing environment variable or a failing health check.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
When the architecture is not the problem#
The same exec format error appears in two other situations worth ruling out once the architectures match:
- A shell script entrypoint with no shebang line. Without
#!/bin/shas its first line, the kernel does not know how to execute the file and fails the same way. Add the shebang and keep the file executable. - A binary copied in from the build host. A
COPYof a tool you compiled on your Mac brings a macOS or arm64 binary into a Linux x86 image. Build such tools inside the image, in a build stage, instead.
The trap is not specific to Fargate either. The same build-machine-versus-runtime mismatch is behind Prisma's "query engine library for current platform" error, where the client is generated for one platform and run on another. For the Compose side of a containerised dev setup, see the Docker development environment tutorial. If you deploy a Next.js app in a container, the runtime also has to meet the framework's floor: see the minimum Node.js version for Next.js 15 before you pick a base image, and the production deployment guide for Next.js and Supabase for the rest of the checklist.
Build for the platform you run on, check the image before it ships, and this error does not come back.
Further reading: Scaleway vs AWS S3: EU Pricing & Data Residency 2026.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix Docker Compose 'Exec format error' loading a library
Your app crashes on docker-compose up while it works fine outside Docker — here is why a native module refuses to load, and the three fixes that actually remove it.
Fix: Sequelize authenticate() Hangs With No Success or Error
Your container logs "start connect" and then nothing — no success, no error. The connection is not failing silently; it is still trying, because pg never times out by default.
Fix OCI runtime exec failed: executable file not found
The container is fine; the command you asked for is not in it. Find out what the image really ships, then install the tool or bring it in from outside.


