The Container Lifecycle
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.
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.
$ 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.
- 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.
- Assuming a stopped container is gone and re-running
docker runfor what should have been adocker start— you accumulate dozens of exited containers, each holding a writable layer and an old log, untildocker ps -ais unreadable and the disk fills. - Using
docker kill(or a too-short stop timeout) as the routine way to stop the DriftwooddbPostgres container — SIGKILL gives Postgres no chance to flush and close cleanly, risking a crash recovery on the next start. - Expecting
docker startof 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 newdocker run, not astart. - Confusing
pausewithstopto "free up" a host — a paused container still holds its memory and its place; it has released no resources, so pausing frees nothing useful.
- Reach for
docker run --rmon one-off and interactive containers so they clean themselves up the instant they exit instead of leaving exited debris behind. - Use
docker stop, notdocker kill, as your default so the process always gets its SIGTERM grace window — reservekillfor containers that are genuinely stuck. - Read
docker ps -aand 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 pruneon long-lived hosts so old exited containers and their writable layers don't quietly consume disk.
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 rmit - 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
-aflag 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