Inside The AI Stack

Container Startup Failures — One Guide to the Whole Class

Exec format errors, permission denied, missing commands, bad entrypoints, immediate exits, and CrashLoopBackOff — diagnosed as one family rather than a page per error string.

intermediateDockerContainersKubernetes
ByJames JoynerPublished Verified 4 min read

There are perhaps a dozen distinct reasons a container fails to start, and roughly a hundred error strings they can produce. Writing a page per error string would be easy and would leave you worse off, because the string you got is often not the one that names your actual problem.

This covers the whole class, in the order the checks should be run.

Start here, always

# Docker: why did it exit, and what did it say?
docker ps -a --filter "name=<name>" --format '{{.Status}}'
docker logs --tail 50 <container>
docker inspect --format '{{.State.ExitCode}} {{.State.Error}}' <container>

# Kubernetes: the previous container's logs survive the restart
kubectl describe pod <pod> | sed -n '/Events:/,$p'
kubectl logs <pod> --previous --tail=50
kubectl get pod <pod> -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}'

The exit code narrows it fast

Code Meaning Where to look
0 Exited normally The command completed. Usually a foreground/background problem
1 Application error Your logs. This is an application failure, not a container one
2 Shell misuse Malformed command, usually in shell-form CMD
126 Found but not executable Permissions or missing execute bit
127 Command not found Wrong path, or missing from the image
137 SIGKILL OOM kill, or a failed graceful shutdown after SIGTERM
139 Segmentation fault Often architecture mismatch or a broken binary
143 SIGTERM Terminated cleanly by the orchestrator

Codes 137 and 143 are the pair most often confused. 143 means the process shut down when asked. 137 means it did not, and was killed — either by the OOM killer or after ignoring SIGTERM through the grace period.

The seven causes

1. The process is not in the foreground

The container’s lifetime is the lifetime of PID 1. If your command starts a service and returns, the container exits with code 0 and everything looks fine except that nothing is running.

CMD ["nginx"]                          # daemonises, container exits
CMD ["nginx", "-g", "daemon off;"]     # stays in the foreground

Anything with a -d, --daemon, --background, or & is suspect. Exit code 0 with no error is the signature.

2. Exec format error

exec /app/server: exec format error

Almost always an architecture mismatch: an image built for amd64 running on arm64, or the reverse. Increasingly common as ARM hosts become normal.

docker inspect --format '{{.Architecture}}/{{.Os}}' myimage:tag
uname -m

The other cause is a script with no shebang line, or with a shebang carrying Windows line endings — #!/bin/sh\r is not a valid interpreter path, and the error message will not mention the \r.

file ./entrypoint.sh    # look for "CRLF line terminators"

Fix the build to target the right platform with --platform, or normalise line endings and add the shebang.

3. Permission denied

exec /app/server: permission denied

Either the file is not executable, or the user cannot reach it.

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

If the executable bit is set, the problem is the user. A USER 10001 that cannot traverse a directory owned by root with restrictive permissions produces exactly this. Check the full path, not just the file.

4. Command not found

exec: "/app/server": stat /app/server: no such file or directory

The file is not where the image thinks it is. Look, rather than reason about it:

look inside the imageillustrative
$ docker run –rm –entrypoint sh myimage:tag -c ‘ls -la /app’# If sh is missing (distroless), inspect the layers instead:$ docker create –name tmp myimage:tag && docker export tmp | tar -tv | grep server

Common causes: a multi-stage COPY --from pointing at the wrong path, a build that silently produced nothing, or a binary that exists but was dynamically linked against libraries the final image does not have. That last one produces “not found” for a file you can see, which is confusing until you know it — the missing thing is the loader, not the binary.

docker run --rm --entrypoint ldd myimage:tag /app/server

5. OOM kill (137)

The container exceeded its memory limit and was killed.

kubectl get pod <pod> -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}'
# OOMKilled

If it dies during startup, the limit is below what the application needs to initialise. If it dies later under load, the limit is below its working set.

Runtimes that are not container-aware are a specific trap: a JVM or similar that reads host memory rather than the cgroup limit will size its heap for the machine and get killed by a limit it does not know about.

6. Configuration or dependency failure (exit 1)

The container started, the application ran, and it exited. This is an application failure and the logs will say so — a missing environment variable, an unreachable database, an unparseable config file.

In Kubernetes this presents as CrashLoopBackOff with increasing backoff. The loop is a symptom; the cause is in --previous logs.

7. Killed by a probe before it was ready

The application is fine and needs longer to start than the probe allows. The liveness probe fails, the container is killed, and it restarts — forever.

startupProbe:
  httpGet: { path: /healthz, port: 8080 }
  failureThreshold: 30
  periodSeconds: 10        # allows up to 5 minutes to start

livenessProbe:
  httpGet: { path: /healthz, port: 8080 }
  periodSeconds: 10        # only begins after startupProbe succeeds

A startupProbe is the correct answer. Loosening the liveness probe to accommodate startup leaves you unable to detect a genuinely hung process later.

The decision tree

Container will not stay running.

Exit code?
├─ 0    → process is not in the foreground, or the command completed
├─ 1    → application error. Read --previous logs; this is not a container problem
├─ 126  → found but not executable → chmod, or the user cannot traverse the path
├─ 127  → not found → wrong path, missing binary, or missing shared library
├─ 137  → killed
│         ├─ terminated.reason == OOMKilled → memory limit
│         └─ otherwise → ignored SIGTERM through the grace period
├─ 139  → segfault → architecture mismatch or broken binary
├─ 143  → clean SIGTERM. Something asked it to stop; find out what
└─ exec format error → architecture mismatch, or shebang / CRLF

No exit code, restarts forever, logs stop mid-startup?
└─ probe killing it before ready → add a startupProbe

Validating a fix

# It stays up
docker run -d --name check myimage:tag && sleep 5 && docker ps --filter name=check

# It stops promptly (proves SIGTERM is reaching the process)
time docker stop check

# In Kubernetes: no restarts after the change
kubectl get pod <pod> -w
kubectl get pod <pod> -o jsonpath='{.status.containerStatuses[*].restartCount}'

A restart count that stays at its current value for several minutes under normal load is the confirmation. A pod that is Running but whose restart count is still climbing has not been fixed.

Verification status

This resource has not been executed end to end in a lab environment. Commands and configuration are reviewed by an engineer, but treat them as reference rather than as a tested procedure.

Author

James Joyner

Builds and operates the infrastructure layers underneath production AI systems.

James founded Inside The AI Stack to publish the kind of infrastructure and operations material he wanted while running production systems: specific, tested where it claims to be tested, and written by someone who has had to fix the thing at 3am. He works across AI infrastructure, private cloud, and platform engineering, and reviews every technical resource published here before it is marked as verified.

  • AI infrastructure
  • OpenStack operations
  • Kubernetes
  • Terraform
  • Linux systems engineering
  • Observability

Primary sources

Related resources chosen because they are the next thing you would actually need — not because they share a keyword.

Guide

Docker Production Engineering

Building container images for production: reproducible builds, correct signal handling, non-root runtime, layer strategy, and keeping secrets out of image history.

intermediate· 4 minDockerContainers
Guide

Kubernetes Workload Troubleshooting

A single diagnostic path for workloads that will not schedule, will not stay up, or keep getting evicted — driven by events and previous logs rather than guesswork.

intermediate· 3 minKubernetes
Lab

Lab: CrashLoopBackOff

A service crash-looping after a routine config change, with logs that show a normal startup and no error anywhere. Work out what is killing it.

intermediate· 25 minKubernetes

Newsletter

Inside The AI Stack Brief

A practical weekly briefing on AI engineering, infrastructure, production operations, and the technologies powering the AI stack.

One email a week. No sponsorship placements inside the technical sections. Unsubscribe in one click.