Exit code 127 Fatal Docker & Kubernetes

Exit Code 127 (Command Not Found) in Docker & Kubernetes

Verdict · 127 = command not found (ENOENT)

Exit code 127 means the shell or runtime could not find the command it was asked to run. In containers the usual causes are a binary absent from the image, a broken symlink, a shell that does not exist (bash on Alpine), or a glibc-linked binary whose dynamic loader is missing on musl.

Observed exit127
Signal / meaningCommand Not Found
Default actionFatal

Proof commands: confirm the exit status and the killer

Run these before changing anything. The first two identify what the runtime recorded; the third finds a kernel-level kill on the host. Replace the placeholder container ID, pod name or host path with your own.

Docker inspect

docker inspect <container_id> --format '{{.State.Status}} | ExitCode: {{.State.ExitCode}} | Error: {{.State.Error}}'

Kubernetes state

kubectl get pod <pod_name> -o jsonpath='{range .status.containerStatuses[*]}{.name}{"\t"}{.lastState.terminated.reason}{"\tExit:"}{.lastState.terminated.exitCode}{"\n"}{end}'

Lookup inside the image

docker run --rm --entrypoint /bin/sh <image> -c 'command -v bash; command -v app; echo PATH=$PATH; ldd /app/server 2>&1 | head'

The error message distinguishes the cases: exec: "bash": executable file not found in $PATH is a missing command; /bin/sh: ./app: not found while the file exists is usually a missing dynamic loader. If ldd prints not found for libc.so.6 or ld-linux, the image is musl-based and the binary is glibc-linked.

Three production root causes

The shell or runtime could not find the command it was asked to run. Confirm the command inside the image with command -v and inspect ldd output for not found libraries. Alpine ships /bin/sh, not bash, and a glibc binary without its musl loader reports not found even when the file exists.

1. The binary or shell is absent from the image

Alpine includes BusyBox /bin/sh but not bash. Distroless images include the application and almost nothing else, so kubectl exec -- /bin/sh fails too. A CMD of ["bash", "-c", ...] therefore exits 127 before any app code runs. Use an explicit absolute path and install only the runtime dependencies the image actually needs.

2. A glibc binary is running on Alpine's musl libc

The kernel needs /lib64/ld-linux-x86-64.so.2 to start a dynamically linked glibc binary. Alpine has no such loader, so the shell reports the file as not found even though it is present and executable. Either build a static binary, use a glibc base such as debian-slim, or install libc6-compat for simple cases; gcompat does not make every glibc binary work.

3. Broken symlink or overwritten PATH

A package upgrade can leave update-alternatives pointing at a removed binary. COPY into a path that is not on PATH, or an ENV PATH that drops /usr/local/bin, produces the same result. Check command -v and readlink -f for the exact name the entrypoint invokes.

Copy-ready fixes for Docker Compose and Kubernetes

The two manifests set explicit memory limits and a termination grace period. Use them as a baseline, then adjust the values to the measured working set of the service.

Compose

docker-compose.yml

Override command only to select a binary that exists in the image. A Compose override that runs bash against an Alpine image fails the same way every time, regardless of restart policy or memory limits.

services:
  app:
    image: ghcr.io/acme/app:1.4.2
    restart: on-failure:3
    stop_grace_period: 10s
    deploy:
      resources:
        limits:
          memory: 256M
        reservations:
          memory: 128M
Kubernetes

deployment.yaml

Kubernetes command and args replace the image's values. If an overlay sets command: ["bash", ...] for an Alpine image, the container exits 127 even though the default image entrypoint works. Verify the final rendered pod spec, not only the Dockerfile.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      terminationGracePeriodSeconds: 20
      containers:
        - name: app
          image: ghcr.io/acme/app:1.4.2
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "256Mi"
Dockerfile

Add the missing shell and glibc compatibility layer

Use only when the binary genuinely needs glibc; a static Go build is smaller and safer.

FROM alpine:3.20
RUN apk add --no-cache bash libc6-compat
COPY --chmod=0755 app /usr/local/bin/app
ENTRYPOINT ["/usr/local/bin/app"]

Frequently asked questions

Why does my container exit 127 when the binary exists?

The binary may be dynamically linked against glibc while the image uses musl. The kernel cannot find the interpreter named in the ELF header and the shell reports not found. Run ldd on the binary inside the image; missing libc.so.6 or ld-linux confirms the mismatch.

How do I run bash in an Alpine container?

Install it: apk add --no-cache bash, then use an explicit bash shebang or ENTRYPOINT ["/bin/bash", ...]. Alpine's default /bin/sh is BusyBox ash and does not support bash syntax. For images you do not control, use /bin/sh and POSIX syntax.

Is exit code 127 always a missing command?

Almost always, but the message matters. command not found means the lookup failed. If the file exists and the message still says not found, suspect a missing dynamic loader. A typo in a Compose command or Kubernetes args is the simplest cause and should be checked first.

Follow the status you actually observed, then check the related port or calculator when the failure involves networking, exposure or firewall rules.

Reference basis: POSIX signal semantics (128 + n), Docker container exit status conventions, and Kubernetes container termination behaviour. Verify the exact behaviour against your runtime version before relying on it in an incident.

Last reviewed 2026-11-06 · All exit codes · Developer tools · Privacy