Fix: Docker exec /entrypoint.sh: no such file or directory
Part of: Docker, DevOps & Infrastructure
Quick Answer
How to fix Docker entrypoint not found error caused by wrong file path, Windows line endings, missing shebang, wrong base image, and multi-stage build issues.
The Error
You run a Docker container and it immediately exits with:
exec /entrypoint.sh: no such file or directoryOr variations:
exec /app/start.sh: no such file or directorystandard_init_linux.go:228: exec user process caused: no such file or directoryexec /usr/local/bin/docker-entrypoint.sh: no such file or directoryOCI runtime create failed: container_linux.go:380: starting container process caused: exec: "/entrypoint.sh": stat /entrypoint.sh: no such file or directoryDocker cannot find or execute the entrypoint script. The file either does not exist in the container, has wrong line endings, is not executable, or references a missing interpreter.
Why This Happens
When Docker starts a container, it runs the ENTRYPOINT or CMD command. If the specified file cannot be found or executed, the container fails immediately.
The confusing part: this error can appear even when the file exists in the container. The “no such file or directory” message is the literal text of ENOENT, the Linux error returned by the execve(2) syscall when any of the path components it needs to resolve are missing. The kernel does not distinguish between “the executable does not exist” and “the interpreter referenced in the executable’s shebang line does not exist” — both surface as the same error. That single ambiguity is the reason this error is so often misdiagnosed.
So no such file or directory can refer to:
- The script itself — the file path is wrong or the file was not copied.
- The interpreter — the shebang line (
#!/bin/bash) references a binary that does not exist in the image. - A linked library — the binary exists but depends on a shared library that is missing.
- Windows line endings —
\r\nline endings corrupt the shebang line, making the interpreter path invalid.
Common causes:
- Windows line endings (CRLF). The most common cause. The file was edited on Windows and has
\r\nline endings. - File not copied into the image. The
COPYorADDinstruction has the wrong path. - Wrong base image. The script uses
#!/bin/bashbut the image only has#!/bin/sh(Alpine Linux). - File not executable. The script does not have execute permissions.
- Multi-stage build. The file was in a build stage but not copied to the final stage.
- Binary compiled for wrong architecture. An amd64 binary running on an arm64 image.
The fastest way to bisect causes 1–4 is to override the entrypoint and shell in: docker run --rm -it --entrypoint /bin/sh myimage then ls -la /entrypoint.sh && head -1 /entrypoint.sh. If the file is missing, it is cause 1. If the file exists but head -1 shows #!/bin/bash\r or a path that does not exist in the image, it is cause 4 or 3 respectively. If the shebang is fine but the file is not executable, the permissions need a chmod +x.
In Production: Incident Lens
In production this is a pod crash loop on startup, and it is one of the highest-blast-radius deploy failures because it happens before any health check runs. The typical pattern: a new image was built in CI, tagged :latest or pushed under a new SHA, the orchestrator pulls it, the container exits immediately with no such file or directory, the orchestrator restarts it, and after N restarts the pod enters crash-loop backoff. Every pod of this service is in the same state because they all run the same broken image. The blast radius is “no pods of this service can start at all” — the existing pods continue to serve traffic until they get evicted, then the service is down.
The monitoring signal is liveness probe failure across the replica set within seconds of a new rollout, combined with a non-zero container restart count growing at the orchestrator’s backoff rate (1s, 10s, 20s, 40s, … 5m). On Kubernetes, the pod status is CrashLoopBackOff with the container’s last termination reason Error and exit code 127 (command not found) or 126 (permission denied). On ECS, the task definition cycles through stop and start with the same exit code. On a plain Docker host with restart: unless-stopped, you see the container appearing and disappearing in docker ps -a.
Recovery is rollback to the previous image SHA — not to :latest, which still points at the broken image, but to the specific SHA digest that was working before. Postmortem preventives are an image smoke test in CI that builds the image, runs it for 10 seconds with a no-op CMD, and verifies the container does not exit with a non-zero code before tagging the image as deployable. Add RUN /entrypoint.sh --version or equivalent to the Dockerfile so the build itself fails if the script cannot execute — that turns a runtime crash into a build-time error. A .gitattributes rule pinning shell scripts to LF and a pre-commit hook running dos2unix --info on staged .sh files prevent the CRLF variant entirely.
Fix 1: Fix Windows Line Endings (CRLF → LF)
The most common cause. Windows text editors add \r (carriage return) characters that break the shebang line:
#!/bin/bash\r ← The \r is invisible but the kernel looks for "/bin/bash\r" which doesn't existFix in the Dockerfile:
COPY entrypoint.sh /entrypoint.sh
RUN sed -i 's/\r$//' /entrypoint.sh && chmod +x /entrypoint.shFix before building — convert on your machine:
# Using dos2unix
dos2unix entrypoint.sh
# Using sed
sed -i 's/\r$//' entrypoint.sh
# Using tr
tr -d '\r' < entrypoint.sh > entrypoint_fixed.sh && mv entrypoint_fixed.sh entrypoint.shFix with .gitattributes (permanent):
# Force LF line endings for all shell scripts
*.sh text eol=lf
entrypoint.sh text eol=lf
Dockerfile text eol=lfFix in VS Code: Click on CRLF in the bottom status bar and switch to LF.
Pro Tip: Always add a
.gitattributesfile to your project with*.sh text eol=lf. This prevents Git from converting line endings to CRLF on Windows checkouts, which is the root cause of most Docker entrypoint issues on Windows.
Fix 2: Verify the File Exists in the Image
The file might not be copied correctly:
Debug — check what is in the image:
# Run a shell in the image to inspect
docker run --rm -it --entrypoint /bin/sh myimage
# List the file
ls -la /entrypoint.sh
# Or use docker inspect
docker run --rm --entrypoint ls myimage -la /entrypoint.shCheck your COPY instruction:
# Wrong — source path is wrong
COPY ./scripts/entrypoint.sh /entrypoint.sh
# The file might be at ./entrypoint.sh, not ./scripts/
# Fixed — verify the path relative to the build context
COPY entrypoint.sh /entrypoint.shCheck .dockerignore:
# If .dockerignore contains this, the file is excluded from the build context!
*.sh
entrypoint.shRemove the entry from .dockerignore or add an exception:
*.sh
!entrypoint.shFix 3: Fix the Shebang Line
The script’s shebang must reference an interpreter that exists in the image:
Alpine Linux does not have bash by default:
#!/bin/bash ← Does NOT exist in Alpine
#!/bin/sh ← Works in Alpine (uses ash/busybox)Fix: Use sh instead of bash:
#!/bin/sh
set -e
echo "Starting application..."
exec "$@"Fix: Install bash in Alpine:
FROM alpine:3.19
RUN apk add --no-cache bash
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]Fix: Use a Debian-based image that includes bash:
FROM debian:bookworm-slim
# bash is available by defaultCommon Mistake: Developing scripts on a machine with bash and deploying to Alpine containers that only have sh. If your script uses bash-specific features (arrays,
[[ ]],${var//pattern/replacement}), either install bash or rewrite using POSIX sh syntax.
Fix 4: Fix File Permissions
The entrypoint script must be executable:
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]Or set permissions before copying:
# On your machine
chmod +x entrypoint.sh
git add entrypoint.sh
git commit -m "Make entrypoint executable"Using COPY with —chmod (Docker BuildKit, Docker 20.10+):
# syntax=docker/dockerfile:1
COPY --chmod=755 entrypoint.sh /entrypoint.shFix 5: Fix Multi-Stage Build Issues
In multi-stage builds, files from earlier stages are not automatically available:
Broken:
FROM node:22 AS build
COPY entrypoint.sh /entrypoint.sh
RUN npm run build
FROM node:22-alpine
# entrypoint.sh is NOT here — it was in the build stage
ENTRYPOINT ["/entrypoint.sh"]Fixed — copy from the build stage:
FROM node:22 AS build
COPY entrypoint.sh /entrypoint.sh
RUN npm run build
FROM node:22-alpine
COPY --from=build /entrypoint.sh /entrypoint.sh
COPY --from=build /app/dist /app/dist
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]Or copy directly in the final stage:
FROM node:22 AS build
RUN npm run build
FROM node:22-alpine
COPY entrypoint.sh /entrypoint.sh
COPY --from=build /app/dist /app/dist
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]Fix 6: Fix Binary Architecture Mismatch
A binary compiled for the wrong CPU architecture triggers the same error:
# amd64 binary on arm64 platform (Apple Silicon Mac)
exec /app/myserver: no such file or directoryCheck the binary architecture:
file myserver
# myserver: ELF 64-bit LSB executable, x86-64 ← amd64Fix: Build for the correct platform:
# Build for amd64
docker build --platform linux/amd64 -t myimage .
# Build for arm64
docker build --platform linux/arm64 -t myimage .
# Build multi-platform
docker buildx build --platform linux/amd64,linux/arm64 -t myimage .For Go binaries:
GOOS=linux GOARCH=amd64 go build -o myserverFor statically linked binaries in scratch/distroless:
# Ensure static linking
CGO_ENABLED=0 GOOS=linux go build -a -o myserverFix 7: Fix ENTRYPOINT Syntax
Docker has two ENTRYPOINT forms. The wrong form can cause issues:
Exec form (recommended):
ENTRYPOINT ["/entrypoint.sh"]Shell form (runs through /bin/sh -c):
ENTRYPOINT /entrypoint.shThe exec form does NOT use a shell. If your script needs shell features, either:
- Use the shell form.
- Or explicitly call the shell:
ENTRYPOINT ["/bin/sh", "/entrypoint.sh"]CMD with ENTRYPOINT:
ENTRYPOINT ["/entrypoint.sh"]
CMD ["--default-flag"]
# Runs: /entrypoint.sh --default-flagMake sure the entrypoint script handles $@ to pass CMD arguments:
#!/bin/sh
set -e
echo "Initializing..."
exec "$@" # Execute the CMD argumentsFix 8: Fix Missing Shared Libraries
For compiled binaries, missing shared libraries cause “no such file or directory”:
# Check what libraries the binary needs
docker run --rm --entrypoint ldd myimage /app/myserver
# libpthread.so.0 => not found
# libc.musl-x86_64.so.1 => not foundFix: Use a compatible base image:
# If compiled on Ubuntu, use Ubuntu (not Alpine)
FROM ubuntu:24.04
COPY myserver /app/myserver
# Or use Alpine if compiled with musl
FROM alpine:3.19
RUN apk add --no-cache libc6-compat # Adds glibc compatibility
COPY myserver /app/myserverFix: Compile statically:
# For Go
FROM golang:1.23 AS build
ENV CGO_ENABLED=0
RUN go build -o /myserver
FROM scratch
COPY --from=build /myserver /myserver
ENTRYPOINT ["/myserver"]Still Not Working?
Inspect the image layer by layer:
docker history myimage
docker inspect myimageUse docker run with a different entrypoint to debug:
docker run --rm -it --entrypoint /bin/sh myimage
# Then manually try running the entrypoint
/entrypoint.shCheck for invisible characters (BOM, zero-width spaces):
hexdump -C entrypoint.sh | head -5
# Should start with 23 21 2f (#!/), not EF BB BF (UTF-8 BOM)Check for a path component owned by a missing user or group. In rootless Docker or in Podman, the container runs as a remapped UID. If your entrypoint lives at /home/appuser/entrypoint.sh and the UID that owns /home/appuser does not exist in the container’s /etc/passwd, execve may return ENOENT instead of EACCES on certain kernel versions. Either move the entrypoint to a path owned by root (/usr/local/bin/) or add the user explicitly:
RUN useradd -ms /bin/sh appuser
USER appuserCheck for a volume mount that shadows the entrypoint at runtime. If your docker run command includes -v /host/path:/ or -v ./:/app and the entrypoint sits at a path under that mount target, the bind mount replaces the in-image content at startup. The image has the file, but the running container sees an empty directory at that path. Run with the mount removed to confirm:
# Without the mount — works
docker run --rm myimage
# With the mount — fails
docker run --rm -v $(pwd):/app myimageIf this is the issue, mount your code somewhere other than the entrypoint’s parent directory.
Check for .dockerignore patterns that match by negation order. A .dockerignore line like **/* followed later by !entrypoint.sh does not always re-include the file if an intermediate pattern excludes its parent directory. Re-include the directory explicitly:
**/*
!entrypoint.sh
!scripts/
!scripts/entrypoint.shCheck for Buildx cache corruption. A stale BuildKit cache layer can preserve a previous state where the file was missing. Force a clean rebuild:
docker buildx build --no-cache --pull -t myimage .For Docker exec format errors when the binary’s architecture is wrong rather than the path, see Fix: Docker exec format error. For Docker container name conflicts after a crashed entrypoint leaves a dead container behind, see Fix: Docker container name already in use. For permission errors that surface as the script being unable to read its own dependencies, see Fix: Docker volume permission denied. For images that exit immediately and end up in a restart loop instead of a clean error, see Fix: Docker container keeps restarting.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Coolify Not Working — Deployment Failing, SSL Not Working, or Containers Not Starting
How to fix Coolify self-hosted PaaS issues — server setup, application deployment, Docker and Nixpacks builds, environment variables, SSL certificates, database provisioning, and GitHub integration.
Fix: Docker Secrets Not Working — BuildKit --secret Not Mounting, Compose Secrets Undefined, or Secret Leaking into Image
How to fix Docker secrets — BuildKit secret mounts in Dockerfile, docker-compose secrets config, runtime vs build-time secrets, environment variable alternatives, and verifying secrets don't leak into image layers.
Fix: Docker Compose Healthcheck Not Working — depends_on Not Waiting or Always Unhealthy
How to fix Docker Compose healthcheck issues — depends_on condition service_healthy, healthcheck command syntax, start_period, custom health scripts, and debugging unhealthy containers.
Fix: Docker Multi-Platform Build Not Working — buildx Fails, Wrong Architecture, or QEMU Error
How to fix Docker multi-platform build issues — buildx setup, QEMU registration, --platform flag usage, architecture-specific dependencies, and pushing multi-arch manifests to a registry.