Docker: OCI runtime create failed — executable file not found in $PATH

Quick answer

The image is fine — Docker just can't find the program you asked it to run. Match your case:

  • Typo, or the binary isn't installed in the image — check with docker run --rm -it myimage sh.
  • Alpine image asking for bash — alpine only has /bin/sh.
  • Distroless/scratch image with shell-form CMD — there's no shell; use exec form.
  • Your entrypoint script isn't executable — RUN chmod +x.
  • Script saved on Windows — CRLF breaks the shebang.

The exact error string

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: exec: "myapp": executable file not found in $PATH: unknown

# older Docker wording, same meaning:
# oci runtime error: container_linux.go:349: starting container process caused
# "exec: \"myapp\": executable file not found in $PATH"

The wall of runtime jargon obscures a simple message. Everything up to the last part is plumbing — containerd's shim, then runc, reporting that it failed to start the container process. The part that matters is at the very end: exec: "myapp": executable file not found in $PATH. Docker successfully pulled the image, created the container, and then tried to launch myapp inside it — and there is no such executable anywhere on the container's PATH.

Which cause is yours?

docker run --rm -it myimage sh sh itself fails too No shell → use exec form (Fix 3) shell opens, but `which myapp` = nothing Typo → Fix 1 Not installed → Fix 2 file exists, won't run chmod +x → Fix 4 script edited on Windows CRLF breaks shebang → Fix 5

Start by trying to open a shell in the image — whether that succeeds already splits the causes in half.

Fix 1: a typo in the command

The most common cause, and the easiest to dismiss too quickly. The name in your CMD, ENTRYPOINT, or docker run argument must match the executable exactly — Linux is case-sensitive, and a stray character is enough. These fail:

# "pyton" — typo
docker run myimage pyton app.py

# capital N — Linux is case-sensitive
docker run myimage Node app.js

Corrected:

docker run myimage python app.py
docker run myimage node app.js

Fix 2: the binary isn't in the image

Slim base images are deliberately minimal, and tools you assume are everywhere often aren't there. curl, git, ps, bash and make are all commonly absent. Verify by opening a shell in the image:

docker run --rm -it myimage sh
/ # which curl
/ #                       ← no output: curl is not installed
/ # echo $PATH            ← see exactly which directories were searched

If it's genuinely missing, install it in the Dockerfile:

# Debian/Ubuntu-based images
RUN apt-get update && apt-get install -y --no-install-recommends curl \
    && rm -rf /var/lib/apt/lists/*

# Alpine
RUN apk add --no-cache curl

Note the same-layer cleanup — installing packages without it silently inflates the image, which the Docker Image Optimizer flags along with the other size and caching issues in a Dockerfile.

Fix 3: the image has no shell (distroless / scratch)

This is the cause that confuses people most, because the command looks obviously correct. Docker's CMD and ENTRYPOINT have two syntaxes, and they behave very differently:

FormWritten asHow it runs
Exec formCMD ["node", "app.js"]Runs the binary directly. No shell involved.
Shell formCMD node app.jsWrapped as /bin/sh -c "node app.js"requires a shell.

On a distroless or scratch base there is no /bin/sh at all, so shell form fails immediately — and the error names your program, not the shell, which is what makes it misleading. This fails, because shell form needs /bin/sh, which doesn't exist here:

FROM gcr.io/distroless/nodejs20
COPY . /app
CMD node /app/server.js

This works, because exec form runs the binary directly with no shell involved:

FROM gcr.io/distroless/nodejs20
COPY . /app
CMD ["node", "/app/server.js"]

The trade-off: exec form gives you no shell features. Variable expansion ($PORT), pipes, && chains and globbing all stop working, because there's no shell to interpret them. If you need those and a minimal image, either move the logic into an entrypoint script (and keep a shell in the image) or resolve the values at build time.

Fix 4: the file exists but isn't executable

If ls shows your script but it still won't run, check the permission bits. COPY does not reliably preserve the execute bit — especially when the file came from a Windows checkout:

/ # ls -l /entrypoint.sh
-rw-r--r--    1 root  root   214 Jul 19 09:00 /entrypoint.sh
#  ↑ no `x` anywhere — it is not executable
# fix it in the Dockerfile so it's baked into the image
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

# or set the bit at copy time (BuildKit)
COPY --chmod=755 entrypoint.sh /entrypoint.sh

The --chmod flag requires BuildKit (the default builder in current Docker, but not universal in older CI images or `DOCKER_BUILDKIT=0` setups) — on the legacy builder it is silently ignored rather than erroring, so the file ends up with the same permissions as before and the fix appears not to have worked. If you're not certain which builder is active, the RUN chmod +x form above works on both.

Also make sure you're using an absolute path, or that the script's directory is actually on PATH. A bare ENTRYPOINT ["entrypoint.sh"] only works if that directory is on the search path; ["/entrypoint.sh"] or ["./entrypoint.sh"] with a matching WORKDIR is unambiguous — see the WORKDIR pitfall below, which is more common than it sounds.

Fix 5: CRLF line endings from Windows

A script written or checked out on Windows has \r\n line endings. The shebang then reads as #!/bin/sh\r, and Linux dutifully looks for an interpreter literally named sh\r — which doesn't exist, producing a not-found error that points at a path looking perfectly correct:

# confirm: a trailing ^M or \r on every line means CRLF
cat -A entrypoint.sh | head -1
# #!/bin/sh^M$

dos2unix entrypoint.sh                    # convert to LF

# stop git from reintroducing it — .gitattributes:
*.sh text eol=lf

The .gitattributes rule is the durable fix; converting the file once only helps until the next checkout on a Windows machine. This same CRLF trap also produces exec format error depending on how the file is invoked, so it's worth ruling out whenever a script-based entrypoint misbehaves.

Fix 6: a relative path with no matching WORKDIR

More common than the CRLF case above, and easy to miss because the file clearly exists. A relative command — CMD ["./app"] or ENTRYPOINT ["./entrypoint.sh"] — is resolved against the container's current working directory, which comes from WORKDIR, not from wherever COPY happened to place the file. If WORKDIR is missing entirely, points somewhere else, or is declared after the CMD/ENTRYPOINT line, the relative path resolves against the wrong directory (often /) and the file isn't there:

# fails — no WORKDIR set, so "./app" resolves against / , not /app
FROM golang:1.22
COPY app /app/app
CMD ["./app"]
# works — WORKDIR makes the relative path resolve where the binary actually is
FROM golang:1.22
COPY app /app/app
WORKDIR /app
CMD ["./app"]

When in doubt, skip the ambiguity and use an absolute path instead — CMD ["/app/app"] works regardless of what WORKDIR is set to.

Fix 7: docker exec into a running container

Everything above assumes you're starting a new container. A large share of this error also shows up on a container that's already running, via docker exec, and it's worth calling out separately because the wording differs slightly:

$ docker exec -it mycontainer bash
OCI runtime exec failed: exec failed: unable to start container process:
exec: "bash": executable file not found in $PATH: unknown

Note exec failed rather than create failed — the container already exists, you're just failing to start a new process inside it. The overwhelmingly common cause is trying to exec into an alpine-based container with bash, which alpine doesn't ship:

docker exec -it mycontainer sh              # works on alpine — use sh instead of bash
docker exec -it mycontainer which bash      # confirms whether bash exists at all

If you specifically need bash for a debugging session, install it in the image (apk add --no-cache bash) rather than assuming it's there — the same fix as the alpine case above, just triggered via exec instead of run.

Not to be confused with: exec format error

These two are neighbours and easy to mix up:

MessageMeaningUsual cause
executable file not found in $PATHThe file could not be locatedTypo, not installed, no shell, wrong path
exec format errorThe file was found but can't be runArchitecture mismatch (amd64 vs arm64), missing shebang

Debugging checklist

Frequently Asked Questions

What does 'executable file not found in $PATH' mean in Docker?

The image was pulled and the container was created, but the OCI runtime could not find the program you told it to run. It searched every directory in the container's PATH and found no matching executable. This is about the command, not the image: the image exists and is fine, the binary named in your CMD, ENTRYPOINT, or docker run argument does not exist inside it.

How do I check what's actually inside the image?

Open a shell in the image and look: docker run --rm -it myimage sh. Then use which <command> or ls -l /path/to/binary to see whether it exists and where. If even sh fails with the same error, the image has no shell at all — that's the distroless/scratch case, and it points at a different fix.

Why does it happen on alpine images specifically?

Alpine ships BusyBox with /bin/sh but no bash. A Dockerfile that specifies bash — CMD ["/bin/bash", "script.sh"] — fails on alpine because /bin/bash doesn't exist. Either use /bin/sh instead, or install bash explicitly with apk add --no-cache bash. The same applies to other GNU tools alpine replaces with BusyBox versions.

What is the difference between exec form and shell form for CMD?

Exec form — CMD ["node", "app.js"] — runs the binary directly with no shell involved, so shell features like variable expansion, pipes and && do not work. Shell form — CMD node app.js — wraps the command in /bin/sh -c, which needs a shell to exist in the image. On a distroless or scratch image there is no shell, so shell form fails with this error while exec form works.

My entrypoint script exists but still isn't found — why?

Two common reasons. First, the file isn't executable: add RUN chmod +x /entrypoint.sh in the Dockerfile, since the execute bit isn't always preserved through COPY. Second, the script has Windows CRLF line endings, so the shebang reads as /bin/sh\r and the loader looks for an interpreter that doesn't exist. Convert it to LF with dos2unix or a .gitattributes rule.

How is this different from 'exec format error'?

They are adjacent but distinct. ‘Executable file not found in $PATH’ means the file could not be located at all. ‘Exec format error’ means the file was found but the kernel cannot run it — usually an architecture mismatch (an amd64 binary on arm64) or a script with no shebang. Not-found points at the path or the image contents; format error points at the binary itself.

Why does docker exec say executable file not found, when the container is already running?

The same cause, a different command: docker exec -it mycontainer bash fails with OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found in $PATH when bash doesn't exist inside that image — most commonly on alpine, which ships only /bin/sh. Try docker exec -it mycontainer sh instead, or install bash in the image if you need it specifically.

Why does a relative command like ./app fail with this error?

A relative path such as CMD ["./app"] or ENTRYPOINT ["./entrypoint.sh"] is resolved against the container's current working directory, which is set by WORKDIR — not by wherever COPY happened to place the file. If WORKDIR is missing, set to the wrong directory, or declared after the CMD/ENTRYPOINT line, the relative path won't resolve and Docker reports the file as not found even though it exists on disk. Set WORKDIR to the directory containing the file, or use an absolute path instead.

More Docker & backend errors

Browse the full reference, or paste your Dockerfile for size and correctness advice.

All Error References Docker: exec format error Docker Image Optimizer
About the author

Pasindu Ishan is a software developer based in Sri Lanka. He builds privacy-first developer tools at JSON Dev Tools.