Container lifecycle, logs and debugging

States and restart policies, where stdout goes, exec versus attach, inspect and exit codes, and the reflex that finds the fault quickly.

States and restart policies

StateMeansHow you get out
createdExists but never starteddocker start
runningPID 1 is alivedocker stop
pausedFrozen by the freezer cgroupdocker unpause
exited (N)PID 1 finished with status Ndocker start or docker rm
restartingRestart policy is looping on a crashRead the logs, fix the cause
deadThe runtime cannot clean it updocker rm -f
docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'
docker inspect -f '{{.State.Status}} {{.State.ExitCode}} {{.State.OOMKilled}}' app
docker inspect app --format '{{json .HostConfig.RestartPolicy}}'

docker stop app           # SIGTERM, then SIGKILL after the grace period
docker stop -t 30 app     # give it 30 seconds to flush
docker start app          # same configuration as the original run
Restart policyBehaviour
noNever restart - the default
on-failure[:max]Restart only on a non-zero exit
alwaysRestart even after a manual stop, and after a daemon restart
unless-stoppedRestart always, except when you stopped it yourself
  • --restart unless-stopped is the usual production choice; always will fight you when you intentionally stop a container for maintenance.
  • A crash loop hides the real error: read the first ten log lines of the first failed attempt, not the noise from the hundredth restart.
  • docker update --restart unless-stopped app changes the policy without recreating the container.

Logs: where they come from

docker logs app                      # everything PID 1 ever wrote
docker logs -f --tail 100 app        # follow the last 100 lines
docker logs -t --since 30m app       # timestamps, last 30 minutes
docker logs --until 2026-09-18T09:00:00 app

docker inspect -f '{{.HostConfig.LogConfig.Type}}' app
ls -lh $(docker inspect -f '{{.LogPath}}' app)   # the file on the host

# rotate it - json-file grows without limit by default
docker run -d --log-opt max-size=10m --log-opt max-file=3 --name web nginx
# or globally, in /etc/docker/daemon.json
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }
  • docker logs shows stdout and stderr only. A process that writes to a file inside the container is invisible here.
  • Applications should log to stdout and let the platform collect it — that is what makes containers portable between engines and orchestrators.
  • The default json-file driver never rotates on its own and is the usual cause of a full /var/lib/docker.
  • Other drivers (journald, syslog, fluentd, gelf) send logs elsewhere, and then docker logs returns nothing — check the driver before you conclude the app is silent.

Getting inside a running container

ToolOpensUse it when
docker exec -it app shA new process in the same namespacesAlmost always - it leaves PID 1 running
docker attach appYour terminal onto PID 1's streamsYou need to interact with the main process itself
docker top appThe process listYou want to see what PID 1 spawned
docker stats appLive CPU, memory, network and I/OSomething is slow or getting killed
docker events --filter container=appThe daemon's event streamYou need to know when it died and who killed it
docker diff appFiles changed in the writable layerYou suspect the app edits itself
docker exec -it app sh
docker exec app env | sort
docker exec app cat /etc/hosts
docker exec app ls -l /app

# use the image's own tools when a container has no shell at all
docker run --rm -it --pid container:app --network container:app nicolaka/netshoot

docker cp app:/var/log/app/error.log ./error.log
docker cp ./config.yml app:/app/config.yml
docker commit app debug-snapshot:1   # last resort: turn a broken state into an image

docker inspect -f '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}' app
⚠️
Everything you change with docker exec or docker cp lives in the writable layer and vanishes the moment the container is replaced. Use it to diagnose, never to deploy: a fix belongs in the image or in a mounted config file, or it will disappear on the next restart.

FAQ

Exit code 0 but the container stopped?
PID 1 completed successfully — that is a normal exit for a one-shot task. If it should have stayed up, the command is not a long-running foreground process. Exit 137 means SIGKILL or an out-of-memory kill, and 143 means it received SIGTERM.
How do I read the logs of a container that keeps restarting?
Read the tail of the current attempt with docker logs --tail 50 app, then the beginning of the failure with docker logs --since around the first crash. Consider pausing the loop with --restart no while you investigate.

Installing Docker and the core CLI workflow Production concerns: resources, health and logging

Last refreshed 2026-09-18.