Docker from Basics to Production · Operate Containers

Debugging Containers

A repeatable checklist for containers that won't start, crash, or misbehave: state, exit code, logs, inspect and a shell.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

Three broken containers (a crash on startup, a wrong command and an out-of-memory kill) diagnosed with the same five commands.

The idea

Work through the same questions every time:

  1. What state is it in? docker ps -a (exited? restarting?).
  2. How did it end? docker inspect --format '{{.State.ExitCode}} {{.State.OOMKilled}} {{.State.Error}}'.
  3. What did it say? docker logs --tail 50 (stdout and stderr).
  4. How is it configured? docker inspect for command, environment, mounts, network and user.
  5. What does it see? docker exec -it NAME sh, or for an image without a shell, docker run --rm -it --entrypoint sh on a debug-friendly variant or docker cp files out.
Exit code Usual meaning
1 Application error (read the logs)
126 Command found but not executable
127 Command not found (typo, missing binary in a slim image)
137 SIGKILL: out of memory or killed after a stop timeout
143 SIGTERM: stopped normally

The code

Diagnose three broken containers
trap 'docker rm -f rc-crash rc-typo rc-oom > /dev/null 2>&1' EXIT
docker run --name rc-crash alpine:3.24 sh -c 'echo "reading config"; echo "fatal: DATABASE_URL not set" >&2; exit 1' > /dev/null 2>&1 || true
docker run --name rc-typo alpine:3.24 pythn app.py > /dev/null 2>&1 || true
docker run --name rc-oom --memory 48m --memory-swap 48m alpine:3.24 sh -c 'tail /dev/zero' > /dev/null 2>&1 || true
for c in rc-crash rc-typo rc-oom; do
  echo "== $c: $(docker inspect $c --format 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}}')"
  docker logs --tail 2 $c 2>&1 | sed 's/^/   log: /'
  err=$(docker inspect $c --format '{{.State.Error}}' | grep -oE 'exec: "[^"]+": executable file not found[^:]*' || true)
  if [ -n "$err" ]; then echo "   error: $err"; fi
done

Output

== rc-crash: status=exited exit=1 oom=false
   log: fatal: DATABASE_URL not set
   log: reading config
== rc-typo: status=created exit=127 oom=false
   error: exec: "pythn": executable file not found in $PATH
== rc-oom: status=exited exit=137 oom=true

Run this on your computer — see “Get Started” for setup.

Common mistakes

  • Restarting a broken container in a loop instead of reading its logs and exit code.
  • "Fixing" things with docker exec in a running container; the next deploy loses the fix.
  • Forgetting that slim images lack tools (no curl, ps, bash): use a debug container or docker cp.
  • Ignoring OOMKilled=true and blaming the application code.

Key points

  • State, exit code, logs, configuration, then look inside.
  • Exit codes 127, 137 and 143 point straight at the cause.
  • Fix the image or configuration, not the running container.

Further reading: docker inspect · docker logs

Practice questions

A container exits immediately with code 127. What do you check?

Answer

That the command exists in the image: 127 means command not found, often a typo or a tool missing from a slim base image.

How do you debug a distroless container that has no shell?

Answer

Read logs and inspect output, copy files out with docker cp, run a debug variant of the image (for example a :debug tag) or attach a separate debug container sharing its namespaces.

Why shouldn't you fix production problems with docker exec?

Answer

Changes made inside a running container are lost when it is replaced and aren't reproducible; the fix belongs in the image or configuration.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?