Overview
In Docker, the message "exec user process caused: no such file or directory" usually appears when the container tries to exec your entrypoint or CMD and the kernel cannot run it. The most common causes are:
- CRLF line endings in scripts copied from Windows
- Missing or wrong shebang (e.g., script lacks
#!/bin/shor points to a non-existent interpreter) - Not executable (missing +x)
- Wrong path in
ENTRYPOINT/CMD
This guide focuses on CRLF and shebang issues and shows quick, reliable fixes for DevOps/Docker workflows.
Quickstart (TL;DR)
- Ensure scripts use LF line endings.
- Add a valid shebang that exists in the image, e.g.,
#!/bin/shor#!/usr/bin/env bash(if bash is installed). - Make scripts executable at build time.
- Use JSON exec form for ENTRYPOINT/CMD.
Commands and Dockerfile hints:
# Convert line endings in your repo (host):
dos2unix entrypoint.sh # or: sed -i 's/\r$//' entrypoint.sh
# Enforce LF in Git (add to .gitattributes):
echo "*.sh text eol=lf" >> .gitattributes
git add .gitattributes entrypoint.sh && git commit -m "Enforce LF for scripts"
# Dockerfile (with BuildKit)
FROM alpine:3.20
COPY --chmod=0755 entrypoint.sh /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
Ensure the script starts with:
#!/bin/sh
set -eu
Minimal working example
Broken example (reproduces the error)
Files:
# Dockerfile
FROM alpine:3.20
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
# entrypoint.sh (saved with CRLF and NO shebang)
echo "It ran"
Build and run:
docker build -t crlf-demo .
docker run --rm crlf-demo
# => exec user process caused: no such file or directory
Why it fails:
- The script has CRLF endings. The kernel reads
\rbefore\n, can’t parse the interpreter line (there isn’t one), and fails to exec.
Fixed example
Make the following changes:
- Add a valid shebang and ensure LF endings:
#!/bin/sh
set -eu
echo "It ran"
- Enforce execute permissions at build time and use JSON exec form:
# Dockerfile (fixed)
FROM alpine:3.20
COPY --chmod=0755 entrypoint.sh /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
Build and run:
docker build -t crlf-demo-fixed .
docker run --rm crlf-demo-fixed
# => It ran
Step-by-step diagnosis
- Confirm the entrypoint path
- Check your Dockerfile:
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]- Ensure the path exists inside the image.
- Quick check:
- Check your Dockerfile:
docker run --rm -it --entrypoint /bin/sh crlf-demo -c \
'ls -l /usr/local/bin/entrypoint.sh || echo missing'
- Check if the file is executable
docker run --rm -it --entrypoint /bin/sh crlf-demo -c \
'ls -l /usr/local/bin/entrypoint.sh'
# Expect: -rwxr-xr-x
If not, fix your Dockerfile to set the mode during COPY or run chmod +x in a build step.
- Detect CRLF line endings
- On host:
hexdump -C entrypoint.sh | head # Look for 0d 0a (\r\n) at line ends
# or
file entrypoint.sh # Often reports "CRLF line terminators"
- Inside the image:
docker run --rm -it --entrypoint /bin/sh crlf-demo -c \
'od -An -t x1 -c /usr/local/bin/entrypoint.sh | head'
# 0d 0a indicates CRLF
If CRLF found, convert to LF and prevent reintroduction (see Git settings below).
- Verify the shebang and interpreter
- First line must be a valid interpreter path present in the image:
- Alpine: use
#!/bin/sh(BusyBox ash) unless you install bash. - If you prefer bash:
#!/usr/bin/env bashandapk add --no-cache bash.
- Alpine: use
- Use JSON exec form to avoid shell quirks
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
# And if you pass args:
CMD ["--verbose"]
Make CRLF problems go away (Git settings)
- In your repo, add
.gitattributes:
*.sh text eol=lf
Dockerfile text eol=lf
- Optionally set per-repo policy:
git config core.autocrlf false
- Normalize existing files (one-time):
git rm --cached -r .
git reset --hard
- If needed, convert current files:
dos2unix entrypoint.sh # or: sed -i 's/\r$//' entrypoint.sh
Common pitfalls and fixes
Missing shebang with exec-form ENTRYPOINT
- Symptom: "exec format error" or the same "no such file or directory".
- Fix: Add
#!/bin/sh(or the interpreter you actually have installed).
Wrong interpreter path
- Example:
#!/bin/bashon Alpine (bash not installed by default). - Fix: Use
#!/bin/shor install bash.
- Example:
Missing execute bit
- Fix at build time:
COPY --chmod=0755 script.sh /usr/local/bin/(requires BuildKit) orRUN chmod +x /usr/local/bin/script.sh.
- Fix at build time:
Hidden CRLF after templating
- Some CI/templating steps reintroduce CRLF. Validate in CI with a check step that fails builds if CRLF is found.
Performance notes
- Set permissions during COPY:
COPY --chmod=0755avoids extraRUN chmodlayers and speeds up builds. - Prevent CRLF at the source (Git attributes) rather than fixing in the image (avoids installing tools like
dos2unixin build stages). - Use multi-stage builds to keep runtime images minimal if you need conversion utilities during build.
- Prefer JSON exec form for
ENTRYPOINT/CMDto avoid spawning extra shells and reduce startup overhead.
Tiny FAQ
Q: Why does it work on my host but fail in Docker?
- A: Your host shell may execute the script via a shell that tolerates CRLF or handles missing shebangs. Docker uses exec form without a shell unless you explicitly invoke one.
Q: How do I verify line endings quickly?
- A:
file script.sh(host) orhexdump -C script.sh | headand look for0d 0a. No0dmeans LF only.
- A:
Q: Do I need bash?
- A: Not usually. Use
#!/bin/shfor portability. If you require bash features, install it and use#!/usr/bin/env bash.
- A: Not usually. Use
Q: I fixed CRLF but still get errors.
- A: Recheck: correct path in
ENTRYPOINT, executable bit set, interpreter exists, and JSON exec form used.
- A: Recheck: correct path in
Q: Can I fix permissions without BuildKit?
- A: Yes, use a
RUN chmod +x /path/script.shlayer after copying the file.
- A: Yes, use a