What the error means
Exit code 127 in Docker builds means command not found. During a RUN step, Docker executes your command with /bin/sh -c (unless you use exec form). If the command or interpreter is missing, or the path is wrong, you get:
executor failed running [/bin/sh -c …]: exit code 127
Quickstart (most common fast fixes)
- Check base image vs package manager:
- Alpine uses apk, not apt-get or yum.
- Debian/Ubuntu use apt-get.
- RHEL/CentOS/Fedora use yum/dnf.
- Install the shell you actually use:
- If you run bash, install it or switch to sh.
- Ensure files exist and are executable:
- COPY the script, chmod +x, and use an absolute path.
- Fix shebangs and line endings:
- Use #!/bin/sh or #!/usr/bin/env bash; convert CRLF to LF.
- Verify PATH and names:
- Use which cmd and echo $PATH inside a debugging RUN.
Minimal working example
Problem: Using apt-get on Alpine (no apt-get).
# Fails with exit code 127
FROM alpine:3.20
RUN apt-get update && apt-get install -y curl
Fix option A: Use a Debian/Ubuntu base where apt-get exists.
# Works
FROM debian:stable-slim
RUN apt-get update && apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
Fix option B: Stay on Alpine and use apk.
# Works on Alpine
FROM alpine:3.20
RUN apk add --no-cache curl
Another common case: relying on bash when it is not installed.
# Fails on Debian slim without bash
FROM debian:stable-slim
RUN bash -lc 'echo hello'
# Fix 1: install bash
FROM debian:stable-slim
RUN apt-get update && apt-get install -y bash \
&& rm -rf /var/lib/apt/lists/*
RUN bash -lc 'echo hello'
# Fix 2: avoid bash-only features; use /bin/sh
FROM debian:stable-slim
RUN /bin/sh -lc 'echo hello'
Step-by-step diagnosis
- Read the failing RUN from the build log
- Rebuild with plain logs for clarity.
docker build --no-cache --progress=plain .
- Confirm the base image and its tools
- Note the FROM line and ensure the commands you run exist there.
- Inspect PATH and command availability
RUN set -eux; \
echo "PATH=$PATH"; \
which bash || true; \
which apt-get || true; \
which apk || true
- Check that scripts are present and executable
COPY scripts/entrypoint.sh /usr/local/bin/entrypoint
RUN set -eux; ls -l /usr/local/bin/entrypoint; file /usr/local/bin/entrypoint; chmod +x /usr/local/bin/entrypoint
RUN /usr/local/bin/entrypoint
- Validate the shebang
- Ensure the first line of the script matches an interpreter in the image, e.g. #!/bin/sh or #!/usr/bin/env bash.
- Normalize line endings if building from Windows
# Using Git
git config core.autocrlf input
# Or convert a file
sed -i 's/\r$//' scripts/entrypoint.sh
- Re-run the specific command in an interactive shell
docker run --rm -it $(docker build -q .) sh -lc 'your failing command here'
Common causes and fixes
Wrong package manager
- Symptom: apt-get: not found on Alpine; yum: not found on Debian.
- Fix: Use the package manager that matches the base image, or change the base image.
Missing shell or interpreter
- Symptom: bash: not found; python: not found.
- Fix: Install the interpreter (apk add bash, apt-get install -y bash, apk add python3) or use a command that exists (sh, python3).
Script not in PATH or not executable
- Symptom: myscript: not found, even after COPY.
- Fix: Use absolute path (/usr/local/bin/myscript), chmod +x, and ensure the directory is in PATH.
Bad shebang
- Symptom: /usr/bin/env: not found or /bin/bash: not found.
- Fix: Update the shebang to an interpreter that exists, or install it.
Windows CRLF endings
- Symptom: may appear as not found or bad interpreter with ^M.
- Fix: Convert to LF before COPY; configure Git to preserve LF.
Exec form vs shell form
- Symptom: Using RUN ["bash", "-lc", "…"] without bash installed.
- Fix: Either install bash or use RUN ["sh", "-lc", "…"].
Multi-stage COPY mistakes
- Symptom: RUN ./tool in stage N but the tool was built in stage M.
- Fix: COPY --from=builder /path/to/tool /usr/local/bin/tool in the final stage.
Base images, shells, and package managers
| Base image | Default shell | Package manager |
|---|---|---|
| alpine | /bin/ash | apk |
| debian/ubuntu | /bin/sh (dash) | apt-get/apt |
| fedora/centos/rhel | /bin/sh (bash) | dnf/yum |
| busybox | /bin/sh | none/built-ins |
Pitfalls to avoid
- Mixing commands from different distros (apt-get on Alpine, apk on Debian).
- Assuming bash exists; many slim images omit it.
- Relying on PATH for scripts placed in non-standard dirs; use absolute paths.
- COPY excluded by .dockerignore; verify the file actually makes it into the image.
- Typographical errors in command names (curl vs cur1) that look like not found.
Performance notes
- Prefer a base image that already has the tools you need to avoid installing heavy dependencies.
- Combine installs in one RUN to reduce layers and keep caches coherent.
- Debian/Ubuntu: RUN apt-get update && apt-get install -y pkg && rm -rf /var/lib/apt/lists/*
- Alpine: RUN apk add --no-cache pkg
- Avoid installing shells you do not need; use /bin/sh where possible.
- Pin package versions for reproducibility; unexpected upgrades can break builds.
Tiny FAQ
What does exit code 127 mean in Docker builds?
- Command not found. The shell could not locate the executable or interpreter.
How do I know which command failed?
- Rebuild with --progress=plain and inspect the RUN line. Add set -eux in the RUN to echo commands.
Why does it work locally but fail in CI?
- Different base images, different line endings, or missing files due to .dockerignore differences.
What is the difference between exit 126 and 127?
- 126: command found but not executable (permissions). 127: command not found.
Can I avoid /bin/sh -c?
- Yes, use the exec form RUN ["/path/to/cmd", "arg"] to bypass the shell, but ensure the command exists.