DockerDocker

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

The container started and the runtime could not find the command it was told to run. How to tell a missing binary from a wrong path or a missing shell.

easy fix7 min read4 causes

the docker error
OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "myapp": executable file not found in $PATH: unknown

docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: exec: "/app/start.sh": stat /app/start.sh: no such file or directory: unknown

exec: "bash": executable file not found in $PATH

Do this first3 steps

Run these in order. Each one tells you what its output means before you change anything.

  1. 1

    Look inside the image without running the failing command

    docker run --rm -it --entrypoint sh myapp:1.0 -c 'ls -l /app && echo "PATH=$PATH"'

    Overriding the entrypoint lets you inspect an image whose normal start command is broken. If ls shows nothing at the expected path, the COPY never landed where you think it did.

  2. 2

    Check the binary is executable and is the right architecture

    docker run --rm --entrypoint sh myapp:1.0 -c 'ls -l /app/server && file /app/server 2>/dev/null || true'

    A file without the x bit gives permission denied rather than not found, and a binary built for another architecture gives exec format error. Both are commonly mistaken for this error.

  3. 3

    Inspect what the image is actually configured to run

    docker image inspect myapp:1.0 --format 'ENTRYPOINT={{.Config.Entrypoint}} CMD={{.Config.Cmd}}'

    Compare this against what exists in the image. The runtime concatenates ENTRYPOINT and CMD, so an unexpected combination here explains most of these failures.

All 9 sections

executable file not found in $PATH means the container was created successfully and then the runtime could not find the program it was told to start. The image is fine. The start command is wrong, or the file it names is not where the runtime is looking.

Note that this is a different failure from exec format error, which means the file was found and the kernel could not run it. Found-but-unrunnable is an architecture problem; not-found is this page.

Look inside the image

You cannot docker run your way in when the entrypoint is what is broken, so override it:

docker run --rm -it --entrypoint sh myapp:1.0

Then check the two things that matter:

ls -l /app
echo "$PATH"

If the binary is not in /app, or /app is empty, the COPY in your Dockerfile did not put it where you think. That is the most common root cause and it is invisible until you look.

If the image has no shell either, see the distroless section below.

Check what the image is configured to run

docker image inspect myapp:1.0 \
  --format 'ENTRYPOINT={{.Config.Entrypoint}} CMD={{.Config.Cmd}}'
ENTRYPOINT=[/app/server] CMD=[--port 8080]

The runtime runs ENTRYPOINT followed by CMD. Getting this combination wrong produces surprising results: with an ENTRYPOINT set, a command you pass to docker run replaces CMD and becomes an argument to the entrypoint, not a new command. That is why

docker run myapp:1.0 bash

on an image with ENTRYPOINT ["/app/server"] tries to run /app/server bash rather than giving you a shell.

Cause 1: The COPY destination is not what you assumed

WORKDIR /app
COPY build/server .
CMD ["./server"]

Looks right, and the trailing . is the trap. COPY src . copies into the working directory when the source is a file, so this produces /app/server and works. But:

COPY build/ /app

copies the contents of build/ into /app, whereas

COPY build /app

copies the directory itself, producing /app/build/server. One character of difference, and CMD ["/app/server"] then points at nothing.

Be explicit and verify:

COPY build/server /app/server
RUN test -x /app/server

That RUN test -x fails the build rather than shipping an image that cannot start, which is a much cheaper place to find out.

Cause 2: Exec form cannot use the shell

CMD ["./start.sh"]        # exec form: no shell involved
CMD ./start.sh            # shell form: runs via /bin/sh -c

Exec form passes the string straight to execve. There is no shell, which means no $VAR expansion, no &&, no globbing, no PATH search of a relative path. These all fail in exec form:

CMD ["$APP_HOME/server"]              # $APP_HOME is literal
CMD ["./server && ./worker"]          # && is not a thing here
CMD ["node server.js --port $PORT"]   # one giant argv[0]

The last is worth dwelling on: CMD ["node server.js"] looks for a program literally named node server.js, space included. Exec form takes one array element per argument:

CMD ["node", "server.js"]

If you genuinely need shell features, be explicit about it:

CMD ["sh", "-c", "exec ./server --port ${PORT:-8080}"]

The exec matters. Without it the shell stays as PID 1 and does not forward SIGTERM, so your container ignores docker stop and gets killed after the timeout. That shows up later as slow deploys and dropped connections, and it is easy to miss.

Cause 3: The shell or tool is not in the image

Alpine has no bash. It ships BusyBox ash as /bin/sh.

FROM alpine:3.20
CMD ["bash", "-c", "echo hi"]     # exec: "bash": executable file not found

Use sh, or install bash if you need it:

RUN apk add --no-cache bash

The same applies to curl, wget, ps and most of what you assume is present. BusyBox provides cut-down versions of some of these and not others, and the cut-down ones do not always accept the flags you expect.

Cause 4: Distroless and scratch images

gcr.io/distroless/* and scratch contain no shell at all. That is the point: less to exploit. It also means:

  • docker run --entrypoint sh fails, because there is no sh.
  • docker exec -it <container> sh fails for the same reason.
  • Any CMD ["sh", "-c", ...] fails.
  • Health checks that shell out fail.

Only exec form works, naming the binary directly:

FROM gcr.io/distroless/static-debian12
COPY --from=build /out/server /server
ENTRYPOINT ["/server"]

To inspect one, use the :debug variant, which includes BusyBox:

docker run --rm -it --entrypoint sh gcr.io/distroless/static-debian12:debug

Or look at the filesystem from outside without running anything:

docker create --name tmp myapp:1.0
docker export tmp | tar -tv | head -30
docker rm tmp

docker export works on a created-but-never-started container, so it works even when the image cannot start at all. It is the most reliable way to answer "what is actually in this image".

The two errors people confuse with this one

MessageMeaning
executable file not found in $PATHThe file is not there, or not on PATH
permission deniedThe file is there without the executable bit
exec format errorThe file is there and built for another architecture

For the middle one:

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

--chmod on COPY avoids a separate RUN chmod layer, and it survives checkouts on Windows where the source file may arrive without the bit set.

A checklist

  1. docker run --rm -it --entrypoint sh <image> and ls the path.
  2. docker image inspect --format 'ENTRYPOINT={{.Config.Entrypoint}} CMD={{.Config.Cmd}}'.
  3. Not where you expected → your COPY destination is wrong. Add RUN test -x.
  4. Exec form with spaces in one element → split into separate array elements.
  5. Need $VAR or &&CMD ["sh", "-c", "exec ..."], with the exec.
  6. Alpine → there is no bash. Use sh or install it.
  7. Distroless or scratch → no shell exists. Use :debug, or docker export to look inside.
  8. permission denied instead → COPY --chmod=0755.

Frequently Asked Questions

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

The container was created and the runtime then could not find the program it was told to execute. Either the file is not at the path given, or the name was given without a path and is not on PATH. It is specifically not an image-pull or permissions problem, and it is distinct from exec format error, which means the file was found and the kernel could not run it. Inspect the image with docker run --entrypoint sh and list the directory you expect the binary to be in.

Why does CMD ["node server.js"] fail?

Because exec form takes one array element per argument, so that asks for a program literally named node server.js, space included. Write CMD ["node", "server.js"]. The same mistake produces confusing results whenever people write a whole command line as a single string in exec form. Shell form, CMD node server.js, does work because it runs through /bin/sh -c, but then the shell becomes PID 1 and will not forward SIGTERM to your process unless you use exec.

Why can I not use bash in an Alpine image?

Alpine does not ship bash. It provides BusyBox ash as /bin/sh, which covers POSIX shell but not bash extensions such as arrays or [[ ]]. Any CMD ["bash", ...], RUN bash ... or docker exec ... bash fails with exactly this error. Either write POSIX-compatible shell and use sh, or add RUN apk add --no-cache bash. The same applies to tools you may assume are present, such as curl, since Alpine's BusyBox provides only a subset and sometimes with different flags.

How do I debug an image with no shell, like distroless?

Two options. Use the :debug tag of the distroless image, which includes BusyBox, so --entrypoint sh works. Or inspect the filesystem from outside without running anything: docker create --name tmp <image> then docker export tmp | tar -tv. The second works even when the image cannot start at all, because docker create never executes the entrypoint, which makes it the most dependable way to answer what is actually inside a broken image.

What is the difference between "executable file not found" and "permission denied"?

Not found means the path does not resolve to a file at all, usually a wrong COPY destination or a name that is not on PATH. Permission denied means the file exists but does not have the executable bit set, which happens frequently with scripts checked out on Windows or copied from an archive that did not preserve the mode. Fix the second with COPY --chmod=0755 script.sh /script.sh, which avoids an extra RUN chmod layer and does not depend on the mode of the file on the build machine.

Reference and practice

Learn the underlying concept

Other Docker errors