Troubleshooting
Docker & Compose

Fix Docker "exec format error" for a Django Image

Fix the Docker error exec /usr/local/bin/docker-entrypoint.sh exec format error for Django containers: rebuild for the right CPU architecture with buildx, fix Windows line endings and missing shebangs, and verify image platforms before deploy.

Problem statement

The image builds fine on your machine, but the container dies the moment it starts on the server, in CI, or on a cloud service:

exec /usr/local/bin/docker-entrypoint.sh: exec format error

Kubernetes reports CrashLoopBackOff, ECS shows Essential container in task exited, and Cloud Run says the container failed to start. The kernel was asked to execute a file it cannot run. Almost always, the image was built for a different CPU architecture, typically on an Apple Silicon Mac (arm64) and deployed to an x86_64 server (amd64), or the other way round. Less often, the entrypoint script has Windows line endings or no shebang.

Why the kernel refusesBuilt on Macarm64 binariesPushed to registryone platform onlyServer is amd64different CPUKernel refusesexec format errorThe image contains binaries for one CPU type, and the server has another.
  1. Check the image's platform: docker image inspect myapp:latest --format '{{.Os}}/{{.Architecture}}'.
  2. Check the server: uname -m (x86_64 is amd64, aarch64 is arm64).
  3. Rebuild for the target: docker buildx build --platform linux/amd64 -t registry/myapp:1.4 --push .
  4. Or build both: --platform linux/amd64,linux/arm64 for a multi-arch image.
  5. If platforms match, check the entrypoint for CRLF line endings and a #!/bin/sh first line.

Step-by-step solution

1. Compare the platforms

docker image inspect registry.example.com/myapp:1.4 --format '{{.Os}}/{{.Architecture}}'
docker buildx imagetools inspect registry.example.com/myapp:1.4
ssh server uname -m

imagetools inspect lists every platform in a multi-arch image. If it shows only linux/arm64 and the server prints x86_64, you have found the cause.

2. Build for the server's platform

On an Apple Silicon Mac, Docker Desktop builds linux/arm64 by default. Ask for the target explicitly:

docker buildx build --platform linux/amd64 -t registry.example.com/myapp:1.4 --push .

Emulated builds are slower, especially steps that compile Python packages. Building in CI on an amd64 runner avoids emulation entirely and is the more reliable habit.

platform mismatches that cause exec format errorBuilds by defaultTypical targetApple Silicon Mac!linux/arm64amd64 serversIntel laptop or CIlinux/amd64amd64 serversAWS Graviton, Amperelinux/arm64arm64 serversRaspberry Pi 4/5linux/arm64the Pi itselfThe Mac-to-amd64 combination causes most incidentsdjango-deployment.com
Pin the platform in your build command so the result never depends on the laptop.

3. Pin the platform in Compose and CI

Make the platform explicit so it cannot drift:

services:
  web:
    build:
      context: .
      platforms: ["linux/amd64"]
    platform: linux/amd64

In GitHub Actions, docker/build-push-action takes platforms: linux/amd64,linux/arm64 with QEMU set up, or use native arm64 runners for the arm64 half. See Build Multi-Arch Django Docker Images.

4. Check the base image and pinned digests

FROM python:3.12-slim is multi-arch, so it follows the build platform. But FROM python@sha256:... pinned to a single-platform digest forces that architecture. Pin the digest of the multi-arch index, which docker buildx imagetools inspect python:3.12-slim shows at the top, not a platform-specific manifest.

5. Rule out the entrypoint script

If the platforms match and the error names a shell script, the script itself is unreadable:

file docker-entrypoint.sh
head -1 docker-entrypoint.sh | od -c | head -2

with CRLF line terminators means Windows line endings: the kernel looks for /bin/sh\r. Convert with dos2unix or sed -i 's/\r$//', and add *.sh text eol=lf to .gitattributes so git does not convert it again. A script without a #!/bin/sh first line gives the same error.

what to checkWhat you findCause and fixarm64 image, x86_64 hostbuilt on Apple Silicon;buildx --platformbase pinned by digestsingle-platform digest; pin the indexfile says CRLFWindows line endings;dos2unix, gitattributesscript lacks a shebangadd #!/bin/sh as the first linePlatform mismatch first, script format seconddjango-deployment.com
The error message is the same for all four, so check them in this order.

Explanation

Linux decides how to run a file by its first bytes. An ELF binary carries a machine type, and the kernel refuses ELF files built for another CPU with ENOEXEC, which Docker reports as "exec format error". Python images contain compiled binaries for the interpreter and for packages such as psycopg, Pillow, and cryptography, so the whole image is architecture-specific even though your Django code is not. For scripts, the kernel reads the #! line; a stray carriage return makes the interpreter path invalid.

Edge cases / notes

  • Emulation on the server. Installing QEMU binfmt handlers lets an amd64 host run arm64 images slowly. Useful for testing, not for production.
  • Wheels inside the image. Copying a virtualenv built on the host into the image brings host-architecture binaries with it. Always install dependencies inside the build.
  • Mixed clusters. Kubernetes clusters with both amd64 and arm64 nodes need multi-arch images or node selectors.

Next steps

FAQ

Why does the image run on my Mac but not the server?

Your Mac built an arm64 image and runs it natively. The server is amd64 and cannot execute arm64 binaries.

Is building with --platform linux/amd64 on a Mac slow?

Yes, it uses emulation. Building in CI on amd64 runners is faster and more consistent.

Do I need multi-arch images?

Only if you deploy to both architectures, for example arm64 cloud instances and amd64 servers, or want local dev and production to use the same tag.

Can line endings really cause this error?

Yes. A CRLF shebang makes the kernel look for an interpreter named /bin/sh followed by a carriage return, which does not exist.

Keep going