Chapter 3: Running Containers
Topic 13

The Container Lifecycle

LifecycleState

A container is not on or off, and most confusion about Docker comes from not knowing which state a container is in. It moves through a small set of states — created, running, paused, exited, and finally removed — and the commands create, start, stop, kill, pause, and rm are the transitions between them. A stopped container still exists: it still holds its writable layer, its logs, and its exit code, and you can start it again — right up until you remove it.

Get this state machine wrong and the symptoms are predictable. You re-run docker run for what should have been a restart and accumulate dozens of exited containers, each pinning a writable layer on disk. You confuse pause with stop and wonder why a "stopped" container still holds its memory. The fix is to know the states and which command moves a container between them.

The Five States

A container in the created state has its resources allocated and its filesystem assembled, but its process has not started. Running means the main process is alive. Paused means the process has been frozen in place with the cgroups freezer — its memory is intact, it simply gets no CPU. Exited means the process returned and the container stopped, but its writable layer is still on disk and inspectable. After docker rm the container is gone, writable layer and all.

The container state machine and the verbs that move between states
created
running
(paused)
exited
removed

docker ps shows only running containers; docker ps -a shows every container that has not yet been removed, with its state and exit code in the STATUS column. That -a is the flag people forget, and forgetting it is why a container "disappears" the moment it exits — it is exited, not removed, and docker ps -a is where it still lives.

A container that exits is still there until you remove it
$ docker run -d --name driftwood-web driftwood/web
3f9a1c7e2b8d...
$ docker ps
CONTAINER ID   IMAGE           STATUS         NAMES
3f9a1c7e2b8d   driftwood/web   Up 4 seconds   driftwood-web
$ docker stop driftwood-web
driftwood-web
$ docker ps            # nothing — it's exited, not running
$ docker ps -a         # there it is, with its exit code
CONTAINER ID   IMAGE           STATUS                     NAMES
3f9a1c7e2b8d   driftwood/web   Exited (0) 6 seconds ago   driftwood-web

create vs run

docker create produces a container in the created state without starting it; docker run is create plus start in one step. That is why run is what everyone types and create is rare — you reach for create only when you want to wire up a container (attach networks, set up mounts) and start it deliberately later. The two-step form buys you nothing for the common case.

stop vs kill

docker stop sends the main process SIGTERM and waits — 10 seconds by default — before following up with SIGKILL, giving the process a chance to flush buffers, close connections, and exit cleanly. docker kill sends SIGKILL immediately with no grace period: the kernel terminates the process where it stands. The difference is a clean shutdown versus a yanked power cord. Whether the grace window actually helps depends on the process receiving and acting on SIGTERM, which is the entire subject of the next topic on PID 1 and signals.

The Exited Container Persists

When the main process returns, the container becomes exited — not removed. Its writable layer, its captured logs, and its exit code all remain inspectable. docker start revives the same container with the same writable layer and the same image it was created from; a restart is not a fresh instance. This is the trap behind "I rebuilt the image but the container still runs the old code" — start reuses the container as it was, and picking up a new image needs a new docker run, not a start.

Removal and --rm

docker rm deletes a stopped container and its writable layer for good — it is the only verb in the set that destroys state. For containers you run once and discard, docker run --rm removes the container automatically the instant its process exits, which keeps short-lived jobs and interactive shells from piling up as exited clutter. On a long-lived host, docker container prune sweeps up the exited containers that accumulated without --rm, reclaiming the writable layers they were pinning.

stop vs kill vs pause vs rm
  • docker stop — asks the process to exit (SIGTERM, then SIGKILL after the 10s grace window). Use it as your default for a clean shutdown.
  • docker kill — forces the process down now with SIGKILL, no grace period. Use it only when a container is genuinely wedged and will not respond to stop.
  • docker pause — freezes the process in place with the cgroups freezer, holding its memory and its resources. Use it to suspend, not to stop — a paused container has released nothing.
  • docker rm — deletes the stopped container and its writable layer. The only one of the four that destroys state, so it is the only one you cannot undo with a start.
Common Mistakes
  • Assuming a stopped container is gone and re-running docker run for what should have been a docker start — you accumulate dozens of exited containers, each holding a writable layer and an old log, until docker ps -a is unreadable and the disk fills.
  • Using docker kill (or a too-short stop timeout) as the routine way to stop the Driftwood db Postgres container — SIGKILL gives Postgres no chance to flush and close cleanly, risking a crash recovery on the next start.
  • Expecting docker start of a stopped container to pick up an image change — it reuses the same container and the same image it was created from; a new image needs a new docker run, not a start.
  • Confusing pause with stop to "free up" a host — a paused container still holds its memory and its place; it has released no resources, so pausing frees nothing useful.
Best Practices
  • Reach for docker run --rm on one-off and interactive containers so they clean themselves up the instant they exit instead of leaving exited debris behind.
  • Use docker stop, not docker kill, as your default so the process always gets its SIGTERM grace window — reserve kill for containers that are genuinely stuck.
  • Read docker ps -a and the exit code (docker inspect --format '{{.State.ExitCode}}') before re-running, so you know whether a container died, exited cleanly, or is merely stopped.
  • Run docker container prune on long-lived hosts so old exited containers and their writable layers don't quietly consume disk.
Comparable tools Podman mirrors the same lifecycle and the same verbs — create, start, stop, rm nerdctl over containerd exposes the identical state machine containerd · runc the tasks and primitives the transitions actually drive, the same ones Kubernetes runs per host

Knowledge Check

Why does a container still appear under docker ps -a after its process exits?

  • It is in the exited state, not removed — its writable layer and exit code persist until you docker rm it
  • Because its image is still in the local store, the daemon keeps the container listed alongside it until the image is removed
  • It is still running quietly in the background and the -a flag is what reveals those background processes
  • Docker holds every exited container for a 24-hour grace window before auto-removing it from the list

What does docker start do to a stopped container that was built from an image you have since rebuilt?

  • It revives the same container with the original image — the rebuild is ignored
  • It re-resolves the tag on start and automatically picks up the newly rebuilt image the tag now points to
  • It discards the old writable layer and starts the container clean from a fresh copy of the rebuilt image
  • It fails with an image-mismatch error because the image the container was created from has since changed

When should you stop the Driftwood Postgres db container with docker kill rather than docker stop?

  • Only when the container is genuinely wedged and won't respond to stop
  • Always, as your routine shutdown, because it stops the container faster by skipping the 10-second grace wait
  • Whenever Postgres has a write in flight, so that the pending write is committed to disk immediately
  • When you want to reclaim the writable layer's disk space at the same time as stopping the container

What does docker run --rm save you from?

  • Accumulating exited containers — it removes the container the moment its process exits
  • Leaving the pulled image sitting in the local store taking up disk after the container finishes
  • A process being SIGKILLed on stop instead of getting a clean SIGTERM grace window to shut down
  • The container restarting in a tight loop after it crashes repeatedly during startup

You got correct