You copy entrypoint.sh into the image, set it as the ENTRYPOINT, build, run, and get:

$ docker run --rm crlf-demo exec /entrypoint.sh: no such file or directory 

You open a shell in the image and the file is exactly where it should be. The error message is technically true, but it's talking about a different file. Here are three errors from the same family, each reproduced on Docker Engine 29.9, with what they actually mean.

1. no such file or directory: the script has Windows line endings

This one hits almost everyone who builds images on Windows. Look at the bytes of the script:

$ od -c entrypoint.sh | head -n 2 0000000 # ! / b i n / s h \r \n e c h o 0000020 " h e l l o f r o m e n t r 

The first line ends with \r \n (CRLF). When the kernel runs the script, it reads the interpreter from the shebang line up to the \n, so it looks for a program called /bin/sh\r, with an invisible carriage return at the end. That program doesn't exist, and the "no such file" in the error is about the interpreter, not your script.

Fixes, from most to least durable:

  • Tell Git to always check out shell scripts with LF. By default, Git for Windows converts text files to CRLF on checkout. Add a .gitattributes file at the repository root:
 *.sh text eol=lf 

Files you already have checked out keep their CRLF until you convert them once, so also fix the existing script.

  • In your editor, switch the file to LF. In VS Code, click CRLF in the status bar and choose LF.

  • As a safety net in the Dockerfile, strip the carriage returns while building:

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

2. permission denied: the script isn't executable

Same setup, different error:

$ docker run --rm noexec-demo 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 

The file has no execute bit. That's easy to miss when the script was created on a filesystem that doesn't store Unix permissions, or came out of a zip, a download or a CI artifact. Most of that long message is just the layers the error passed through (daemon → containerd shim → runc); the useful part is at the end.

The cleanest fix is to set the mode while copying, which needs no extra layer:

COPY --chmod=755 entrypoint.sh /entrypoint.sh 

On Linux or macOS you can also run chmod +x entrypoint.sh before building, and Git will keep that bit.

3. executable file not found in $PATH: the image doesn't have that shell

$ docker run --rm alpine:3.23 bash 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: "bash": executable file not found in $PATH 

Alpine ships sh (BusyBox), not bash. The same error appears when the program in CMD is misspelled or was never copied into the image.

Use sh, or install bash explicitly:

RUN apk add --no-cache bash 

If your script uses bash-only syntax but starts with #!/bin/sh, it will run under BusyBox sh and fail in other, more confusing ways, so make the shebang match the shell you actually need.

A quick checklist

When a container won't start and the message mentions a file you can see:

  1. od -c script.sh | head -n 1: is there a \r before the \n?
  2. ls -l inside the image (docker run --rm --entrypoint ls image -l /entrypoint.sh): is there an x in the mode?
  3. Does the shebang's interpreter exist in the image? Try docker run --rm --entrypoint sh image -c 'command -v bash'.
  4. Read the error from the end: the last exec: ... part names the thing that actually failed.

I keep a page of nearly 30 common Docker errors like these, each reproduced with real output and a fix, as part of a free Docker tutorial: Docker error messages: causes and fixes.