Debugging Containers From the Terminal: A Practical Docker CLI Workflow
A crashing container pushes you toward the dramatic move — restart, redeploy, rebuild — before you know what's wrong. Most of the time the answer is one cheap command away. This fixed-order sequence starts with the cheapest check and moves up layers. Use it every time.
Step 1: docker ps -a — Status and Exit Code
First question: what state is the container in? Not what you think — what Docker says.
docker ps -a
The -a flag is mandatory. A crashed container doesn't appear in plain docker ps. Look at the STATUS column:
- Up 10 minutes — running; problem is inside a live container (skip to logs).
- Restarting (1) 5 seconds ago — crash loop. The number in parentheses is the last exit code.
- Exited (0) 3 minutes ago — clean stop; maybe no bug.
- Exited (137) or Exited (143) — killed by a signal.
Exit codes decode quickly:
- 0 — clean exit.
- 1 — generic application error.
- 125 —
docker runcommand failed. - 126 — command found but not executable.
- 127 — command not found.
- 137 — SIGKILL (128+9). Most often OOM killer or Docker force-kill.
- 143 — SIGTERM (128+15). Graceful shutdown request.
137 vs 143 matters. 143 is usually expected. 137 is worth investigating.
Step 2: docker logs --tail then -f — What Did It Say?
Read what the container actually said. Start bounded:
docker logs --tail 100 web-1
docker logs --tail 100 -t web-1 # with timestamps
docker logs -f web-1 # follow live
For a crash loop, --tail captures the death rattle. If the container restarts fast, -f lets you watch a fresh crash. Caveat: docker logs only shows stdout/stderr. Empty logs mean either the app died before logging or logs go to a file inside the container.
Step 3: docker inspect --format — State, Restarts, Mounts, Health
If logs don't hand you the answer, ask Docker for structured facts:
docker inspect --format '{{ .State.Status }} {{ .State.ExitCode }}' web-1
docker inspect --format '{{ .RestartCount }}' web-1
docker inspect --format '{{ .State.OOMKilled }}' web-1
docker inspect --format '{{ range .Mounts }}{{ .Source }} -> {{ .Destination }}{{ "\n" }}{{ end }}' web-1
docker inspect --format '{{ .State.Health.Status }}' web-1
.State.OOMKilled turns a guess into a fact. Saw 137 and suspect memory? This returns true or false. RestartCount confirms a real loop. Mounts check catches volume path mismatches.
Step 4: docker exec -it ... sh — Get Inside
If the container is running, step inside:
docker exec -it web-1 sh
# or bash
docker exec -it web-1 bash
Inside, check things logs can't show:
cat /etc/app/app.conf
env | sort
nslookup db-1
wget -qO- http://db-1:5432 || echo "cannot reach db-1"
df -h
If the container already exited, override the entrypoint:
docker run --rm -it --entrypoint sh myapp:1.4.2
Now you're in the filesystem without the failing startup command.
Step 5: docker stats — Resource Pressure
For slow or OOM-killed containers:
docker stats
docker stats --no-stream web-1
Watch MEM USAGE / LIMIT. If memory is pinned against the limit, you've found your 137. High steady CPU explains sluggishness. The slow leak looks like a container that "randomly" restarts every few hours.
Step 6: docker events — Who's Acting on This Container?
If the container dies but nothing inside explains why, widen the lens:
docker events --filter container=web-1
docker events --since 30m --filter container=web-1
This catches external actors — orchestrator killing, health check restarting, a script running docker kill.
Worked Example
web-1 is stuck restarting. docker ps -a shows Restarting (137). docker logs --tail 50 web-1 shows the app booting normally then stopping mid-request, no exception. docker inspect --format '{{ .State.OOMKilled }}' web-1 returns true. docker stats --no-stream web-1 confirms memory at the limit. Fix: increase memory limit or profile the leak. Six commands, no guessing.
Making It Repeatable
The order is the key: status, logs, inspect, exec, stats, events. Write it as a runbook for your team. When something breaks at 3 a.m., the person on call executes a routine, not improvises.
For reference versions of Docker errors, the author keeps guides at devopsaitoolkit.com/categories/docker and a broader toolkit at devopsaitoolkit.com/stacks/docker.
Wrapping up: start cheap, move up one layer at a time. Read the exit code, read the logs, confirm with inspect, then go inside. Fix the order in your head once, and every future incident gets quieter.


