Your container dies the moment it starts, and docker logs shows one line: exec /entrypoint.sh: exec format error. Or the same path with no such file or directory, even though ls inside the image proves the file is there. Both messages come from the kernel refusing to start the container's first process. The cause is either the CPU architecture of the image or the first few bytes of your entrypoint script.

This guide reproduces every common cause on Docker Engine 29.9.3 with alpine:3.24.4 on an x86_64 host, shows the one command that identifies each, and fixes it. All output below comes from that lab run on November 10, 2026. The problem is common. On that day the Stack Overflow question exec user process caused "exec format error" had 423,211 views, exec /usr/bin/sh: exec format error had 407,667 and Why won't my docker-entrypoint.sh execute? had 287,087.

The short version:

  • Run docker image inspect -f '{{.Architecture}}' IMAGE and uname -m. If they differ (arm64 image, x86_64 host), rebuild with --platform linux/amd64 or build a multi-platform image.
  • If the architecture matches, look at the first line of the script with head -1 entrypoint.sh | od -c. It must start with #!/bin/sh and end with \n, not \r\n, with no bytes before #!.
  • no such file or directory for a file that exists means the interpreter is missing: CRLF line endings, #!/bin/bash on Alpine, or a glibc binary on musl.
  • Add *.sh text eol=lf to .gitattributes and use COPY --chmod=755 so the problem doesn't come back.

Table of contents

Why Docker says exec format error

When a container starts, runc asks the Linux kernel to execute the entrypoint. The kernel runs two kinds of files: an ELF binary built for its own CPU, or a text file whose first two bytes are #! followed by the path of an interpreter. Anything else gets exec format error. If the interpreter path in #! doesn't exist, it gets no such file or directory, and the message names your script, not the missing interpreter. That is what makes this error confusing.

Use this map to go from the message to the cause:

Table diagram mapping Docker errors exec format error, no such file or directory and permission denied to causes such as arm64 image on amd64, missing shebang, UTF-8 BOM, CRLF line endings, bash on Alpine, glibc binary on musl and missing execute bit, with a check command and fix for each

Old and new versions of the message

Older Docker releases printed the error through runc's Go source file, for example standard_init_linux.go:178: exec user process caused "exec format error". Docker Engine 29.9.3 prints a shorter form with the path that failed:

exec /entrypoint.sh: exec format error exec /entrypoint.sh: no such file or directory exec /bin/sh: exec format error 

They mean the same thing. The path tells you which file the kernel rejected, and that is your first clue. A system path like /bin/sh or /bin/uname points at the architecture. Your own script points at the script.

Check the architecture first

Run these two commands on the machine where the container fails:

uname -m docker image inspect -f '{{.Os}}/{{.Architecture}}' myapp:arm64only 

In the lab the host said x86_64 and the image said linux/arm64. That mismatch alone explains the error, so fix it before you look at scripts.

Fix an image built for the wrong CPU architecture

The Docker multi-platform docs put it plainly: "Containers share the host kernel, which means that the code that's running inside the container must be compatible with the host's architecture." docker build targets the build machine's platform unless you pass --platform, so an image built on an Apple Silicon Mac or a Raspberry Pi is linux/arm64. Push it, pull it on an x86_64 server, and every binary inside it fails.

Reproduce the mismatch

The lab forced arm64 on the x86_64 host in three ways. Running an arm64 image, building an arm64 image with a RUN step, and running an image that was built for arm64 only:

docker run --rm --platform linux/arm64 alpine:3.24.4 uname -m docker build --platform linux/arm64 -t myapp:arm . docker run --rm myapp:arm64only 

All three failed. The build stopped at the first RUN with exec /bin/sh: exec format error, which is the exact message from SO 73285601. The plain docker run printed a warning first: The requested image's platform (linux/arm64) does not match the detected host platform (linux/amd64/v4) and no specific platform was requested. Look for that warning in your logs.

Terminal capture on an x86_64 host: running alpine 3.24.4 as linux/arm64 prints exec /bin/uname: exec format error, an arm64 build fails at RUN with exec /bin/sh: exec format error, docker image inspect shows linux/arm64, and an amd64 rebuild prints x86_64

Build for the right platform

If the image only needs to run on x86_64 servers, build for that platform. The lab rebuilt the same Dockerfile and it ran:

docker build --platform linux/amd64 -t myapp:amd64 . docker run --rm myapp:amd64 # x86_64 

Setting DOCKER_DEFAULT_PLATFORM=linux/amd64 in your shell does the same for commands without the flag. A lab build with only that variable set came out as amd64. In Compose, the platform key does it per service. With platform: linux/arm64 the lab service printed exec /bin/uname: exec format error and exited with code 255. With platform: linux/amd64 it printed x86_64:

services: app: image: alpine:3.24.4 platform: linux/amd64 command: ["uname", "-m"] 

To serve both Macs and servers, build a multi-platform image. Docker Engine 29 uses the containerd image store by default, so docker build can produce one tag with several platforms. Cross-compiling in a stage pinned to $BUILDPLATFORM avoids emulation:

FROM --platform=$BUILDPLATFORM golang:1.27.3-alpine3.24 AS build ARG TARGETOS TARGETARCH WORKDIR /src COPY . . RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/hello . FROM alpine:3.24.4 COPY --from=build /out/hello /usr/local/bin/hello ENTRYPOINT ["hello"] 
docker build --platform linux/amd64,linux/arm64 -t hello:1.0 . docker image ls --tree hello:1.0 docker run --rm hello:1.0 

The tree listed a linux/amd64 and a linux/arm64 variant, and the run printed hello from linux/amd64. Docker picked the right variant for the host. To check what a published image supports before you pull it, use docker buildx imagetools inspect alpine:3.24.4 | grep Platform:.

Terminal capture: docker build for linux/amd64 and linux/arm64 creates hello 1.0, docker image ls --tree shows both platform variants, the container prints hello from linux/amd64, and imagetools inspect lists the platforms of alpine 3.24.4

The same docs describe running foreign images under QEMU emulation, registered with the tonistiigi/binfmt image. That works for testing, but it is slower than a native image, so a matching build is the better fix for production.

Fix entrypoint scripts that fail with exec format error

If the architecture matches and the error names your own script, the problem is inside the script. The lab built one Dockerfile six times with different versions of entrypoint.sh:

FROM alpine:3.24.4 COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] CMD ["uname", "-m"] 

Two variants failed with exec format error, two with no such file or directory and one with permission denied. Only the clean script ran:

Terminal capture: the same image built with six entrypoint.sh versions, no shebang and BOM give exec format error, CRLF and bash on Alpine give no such file or directory, a non-executable file gives permission denied, and the good script prints hello from entrypoint and x86_64

Missing shebang line

A script without #! is just text to the kernel. Bash runs such a file as a shell script when you call ./entrypoint.sh. In the lab it printed hello from entrypoint without complaint, which hides the bug on your laptop. Docker uses the exec form and has no shell to fall back on. The top answer on SO 42494853, from Marcus Ruddick with 503 votes, is one line: "I forgot to put #!/bin/bash at the top of the sh file, problem solved."

Make #!/bin/sh the first line:

#!/bin/sh echo "hello from entrypoint" exec "$@" 

The same image ran fine when started with --entrypoint sh and the script as an argument. ENTRYPOINT ["sh", "/entrypoint.sh"] works as a quick workaround, but the shebang is the real fix.

A byte order mark before the shebang

Some Windows editors save UTF-8 files with a byte order mark, three invisible bytes before the text. The kernel then sees \357\273\277#! instead of #! and treats the file like it has no shebang. od -c shows it at once:

docker run --rm --entrypoint sh ep:bom -c "head -1 /entrypoint.sh | od -c" # 0000000 357 273 277 # ! / b i n / s h \n 

Save the file as UTF-8 without BOM in your editor, or strip it with GNU sed on your machine before you build:

sed -i '1s/^\xEF\xBB\xBF//' entrypoint.sh 

Don't rely on running that sed inside an Alpine build stage. In the lab, the same command with BusyBox sed in alpine:3.24.4 left the BOM in place, and the image still failed.

Fix exec /entrypoint.sh: no such file or directory

This message names your script, but the missing file is the interpreter in the shebang or, for binaries, the dynamic loader. The file command on your machine identifies all three cases:

Terminal capture: file reports CRLF line terminators and UTF-8 with BOM for the broken scripts, od -c shows a carriage return after /bin/sh and BOM bytes before the shebang, /bin/bash is missing in Alpine, and a Debian uname binary in Alpine needs /lib64/ld-linux-x86-64.so.2

Windows CRLF line endings

A script saved with Windows line endings has a carriage return at the end of every line, including the shebang. The kernel looks for an interpreter called /bin/sh\r, which doesn't exist. file reports with CRLF line terminators and od -c shows the \r:

0000000 # ! / b i n / s h \r \n 

Fix the copy in the image during the build. This worked in the lab with BusyBox sed:

COPY --chmod=755 entrypoint.sh /entrypoint.sh RUN sed -i 's/\r$//' /entrypoint.sh 

Better, stop Git from creating CRLF files. In the lab, cloning with core.autocrlf=true (the usual Windows setting) turned an LF script into a CRLF script. After adding one line to .gitattributes, a fresh clone with the same setting kept LF:

*.sh text eol=lf 

The gitattributes docs use the same *.sh text eol=lf line as their example. Run git add --renormalize . after you add it so files already in the repo get fixed too.

Bash on Alpine and glibc binaries on musl

Alpine ships BusyBox sh, not bash. A script that starts with #!/bin/bash fails with no such file or directory, and ls -l /bin/bash inside the image confirms bash is missing. Change the shebang to #!/bin/sh if the script uses no bash features, or install bash in the image:

RUN apk add --no-cache bash 

Binaries hit the same wall. The lab copied /usr/bin/uname from debian:13.7-slim into alpine:3.24.4, and it failed with exec /app/uname: no such file or directory. file showed why: dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2. Alpine uses musl and has no glibc loader. Three fixes all worked in the lab: run the binary on a Debian base, install the compatibility layer with apk add --no-cache gcompat, or build a static binary as in the Go example above with CGO_ENABLED=0.

Fix permission denied and stop it coming back

A script without the execute bit fails with a different, longer message from the daemon:

docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "/entrypoint.sh": permission denied 

Set the execute bit in the Dockerfile

COPY --chmod=755 sets the mode as the file lands in the image. The lab's broken image ran after that one change. RUN chmod +x /entrypoint.sh works as well. To store the bit in Git, so a checkout from Windows doesn't lose it, run git update-index --chmod=+x entrypoint.sh and commit. In the lab git ls-files -s changed from 100644 to 100755.

A checklist before you push

Run these on any image that will run on another machine:

docker image inspect -f '{{.Os}}/{{.Architecture}}' IMAGE file entrypoint.sh head -1 entrypoint.sh | od -c docker run --rm IMAGE 

The first line must match the server's uname -m. file must not mention CRLF or BOM. The first bytes must be #! and an interpreter that exists in the image. If the container runs locally and fails only on the server, check the architecture first.

Other Docker errors have their own guides: Docker permission denied on docker.sock, port is already allocated, nginx host not found in upstream and server gave HTTP response to HTTPS client for private registries. If your entrypoint initializes a database, see PostgreSQL in Docker: password authentication failed.

FAQ

What does exec format error mean in Docker?

The kernel could not run the container's first process. Either the binary was built for another CPU architecture, or a script lacks a valid #! line at the very start of the file. The path in the message tells you which file failed. See SO 42494853.

Why does an image built on my M1 Mac fail on my server?

docker build targets the platform of the machine it runs on, so an Apple Silicon Mac produces linux/arm64 images. An x86_64 server can't run arm64 binaries without emulation. Build with --platform linux/amd64 or with --platform linux/amd64,linux/arm64. See SO 73398714.

Why does Docker say no such file or directory when the file exists?

The missing file is the interpreter named in the shebang, not your script. CRLF line endings turn /bin/sh into /bin/sh\r, and #!/bin/bash fails on Alpine because bash isn't installed. See SO 51508150.

Why won't my docker-entrypoint.sh execute?

Check four things in order: the image architecture, the shebang line, the line endings and the execute bit. file entrypoint.sh and ls -l cover the last three in seconds. See SO 38905135.

What does $'\r': command not found mean?

The script has CRLF line endings and you ran it through a shell, for example sh entrypoint.sh or bash entrypoint.sh. The shell starts, but every command ends in a stray carriage return. In the lab bash printed cd: $'/tmp\r': No such file or directory. Convert the file to LF with sed -i 's/\r$//' or dos2unix.

Can I run arm64 images on an amd64 host?

Only through emulation. Docker's multi-platform docs describe registering QEMU emulators on the host with the privileged tonistiigi/binfmt image (tag qemu-v10.2.3 at the time of writing). Without it, the lab's arm64 containers failed with exec format error. For production, a native amd64 or multi-platform image is faster.