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.
- Check the image's platform:
docker image inspect myapp:latest --format '{{.Os}}/{{.Architecture}}'. - Check the server:
uname -m(x86_64is amd64,aarch64is arm64). - Rebuild for the target:
docker buildx build --platform linux/amd64 -t registry/myapp:1.4 --push . - Or build both:
--platform linux/amd64,linux/arm64for a multi-arch image. - If platforms match, check the entrypoint for CRLF line endings and a
#!/bin/shfirst 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.
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.
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
- Publish images for both architectures with Build Multi-Arch Django Docker Images.
- If the script runs but is denied, see Fix Docker Entrypoint Permission Denied.
- Review the script itself with Write a Django Docker Entrypoint Script.
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.