Overview
The error /bin/sh^M: bad interpreter in a Linux container means the script file has Windows (CRLF) line endings. Linux expects LF. The extra carriage return (^M) in the shebang (#!/bin/sh) prevents the kernel from finding the interpreter.
This guide shows fast ways to convert line endings, how to detect the problem, and how to prevent it in Docker-based workflows.
Quickstart
- One-time fix (local): convert files to LF using dos2unix or sed.
- In Dockerfile: strip CRLF after COPY using sed, or ensure the build context already has LF via .gitattributes.
- Prevent regressions: add .gitattributes rules and configure Git to avoid CRLF in scripts.
Minimal Working Example (reproduce and fix)
Reproduce the error
Create a Dockerfile that writes a CRLF-terminated script and tries to run it:
# Dockerfile
FROM alpine:3.20
RUN printf '#!/bin/sh\r\necho "Hello from CRLF"\r\n' \
> /usr/local/bin/run.sh \
&& chmod +x /usr/local/bin/run.sh
CMD ["/usr/local/bin/run.sh"]
Build and run:
docker build -t crlf-demo .
docker run --rm crlf-demo
# -> /usr/local/bin/run.sh: /bin/sh^M: bad interpreter: No such file or directory
Fix inside the Dockerfile
Add a conversion step that removes CR characters (\r):
# Dockerfile (fixed)
FROM alpine:3.20
RUN printf '#!/bin/sh\r\necho "Hello from LF"\r\n' \
> /usr/local/bin/run.sh \
&& sed -i 's/\r$//' /usr/local/bin/run.sh \
&& chmod +x /usr/local/bin/run.sh
CMD ["/usr/local/bin/run.sh"]
Rebuild and run:
docker build -t crlf-fixed .
docker run --rm crlf-fixed
# -> Hello from LF
Step-by-step fixes
- Convert line endings locally
- Using dos2unix:
- macOS/Linux:
dos2unix script.sh - Windows (Git Bash/MSYS2/WSL):
dos2unix script.sh
- macOS/Linux:
- Using sed (portable in containers):
sed -i 's/\r$//' script.sh
- Re-run:
./script.shor use in your image.
- Convert in Docker build
- After copying from host:
FROM alpine:3.20
WORKDIR /app
COPY . .
# Convert all shell scripts and entrypoints to LF
RUN find . -type f \( -name "*.sh" -o -name "entrypoint" -o -name "*.env" \) \
-exec sed -i 's/\r$//' {} + \
&& chmod +x /app/entrypoint.sh
CMD ["/app/entrypoint.sh"]
- Alternative with dos2unix (adds a package):
RUN apk add --no-cache dos2unix \
&& find . -type f -name "*.sh" -exec dos2unix {} +
- Fix via Git settings (prevention)
- Add a .gitattributes file to normalize endings:
# .gitattributes
*.sh text eol=lf
*.bash text eol=lf
Dockerfile text eol=lf
*.env text eol=lf
- On Windows, prefer repository-driven EOLs and disable global CRLF conversion:
git config --global core.autocrlf false
- Re-checkout files to apply attributes:
git rm --cached -r . && git reset --hard
- Bypass shebang temporarily
- Running through the interpreter ignores the shebang line ending:
sh script.shorbash script.sh
- Useful for debugging, but not a fix for ENTRYPOINT/CMD.
Detecting CRLF quickly
- file:
file -b script.sh→ “… with CRLF line terminators”
- grep for CR (carriage return):
grep -Irl $'\r' .(lists files containing \r)
- View bytes:
od -An -t x1 -c script.sh | head(look for 0d aka \r)
Where the problem appears in Docker
- COPYing from a Windows host into a Linux image preserves CRLF if present.
- Bind-mounted volumes from Windows into Linux containers often carry CRLF.
- The first line (shebang) is critical; a CR after /bin/sh breaks direct execution.
Hardening your Docker workflow
- Normalize at source with .gitattributes. Example policy:
* text=auto(default text normalization)*.sh text eol=lfandDockerfile text eol=lffor critical files
- Validate in CI:
! grep -Irl $'\r' -- .or a dedicated lint step to fail the build if CRLF is present.
- Normalize during build as a safety net using sed or dos2unix.
- Ensure executable bits are set after copying:
chmod +x entrypoint.sh.
Pitfalls
- Converting binaries: Do not run sed/dos2unix on non-text files. Restrict patterns (e.g., *.sh, Dockerfile).
- Editor settings: IDEs on Windows may reintroduce CRLF. Set per-project EOL to LF for scripts.
- core.autocrlf=true on Windows: This often creates CRLF on checkout. Prefer core.autocrlf=false with .gitattributes enforcing LF for scripts.
- Mixed endings: A file with LF body but CRLF shebang still fails when executed directly.
- Alpine busybox sh vs bash: The error is from the kernel before the shell runs; changing shells won’t help until line endings are fixed.
Performance notes
- Avoid installing dos2unix at runtime; do conversions in the build to keep images small.
- sed-based conversion is fast and requires no extra packages.
- Converting once at build time is cheaper than per-container startup hooks.
- Minimizing extra RUN layers: group find/sed in a single RUN to reduce image layers.
Tiny FAQ
Why does ^M appear?
- ^M represents a carriage return (CR, \r) from Windows CRLF endings. Linux expects only LF (\n).
Why does
sh script.shwork but./script.shfails?- Direct execution uses the shebang; CR there breaks interpreter resolution. Invoking
shbypasses the shebang.
- Direct execution uses the shebang; CR there breaks interpreter resolution. Invoking
Does the base image matter?
- Any Linux base will fail on CRLF shebangs. The fix is to use LF line endings.
Can I fix this without changing files?
- As a workaround, execute via
sh script.sh. For containers (ENTRYPOINT/CMD), you must convert to LF.
- As a workaround, execute via
Best long-term fix?
- Enforce LF via .gitattributes and verify in CI; optionally strip CRLF during Docker builds as a safeguard.