TechEarl

Docker Container Lifecycle: ps, stop, start, restart, rm

Listing, stopping, starting, restarting, renaming, and removing containers without breaking anything. The commands you reach for once docker run is done and the container is sitting there doing its job.

Ishan KarunaratneIshan Karunaratne⏱️ 8 min readUpdated
Share thisCopied

docker run creates and starts containers. Everything that happens after — listing them, stopping them, restarting them, getting rid of them — is the container lifecycle. This article covers the small set of commands that handle all of that: docker ps, docker stop, docker start, docker restart, docker rename, docker rm, plus kill, pause, and the auto-remove --rm pattern.

The lifecycle states

A container moves between these states:

code
created → running → paused → running → exited → removed
                           ↘ exited ↗
  • createddocker create ran but the container hasn't been started. Rare; usually you go straight to running with docker run.
  • running — the main process is alive. docker ps shows it.
  • paused — frozen via cgroups. docker pause and docker unpause.
  • exited — the main process has stopped (either gracefully via docker stop or because it crashed/finished). The container still exists; you can read logs, inspect, restart.
  • removed — the container's filesystem and metadata are gone. docker rm did this.

List running containers — docker ps

bash
# Running containers
docker ps

# All containers, including stopped ones
docker ps -a

# Just the IDs (useful in scripts)
docker ps -q

# All IDs (running + stopped)
docker ps -aq

# Filter by status, name, image, label
docker ps --filter status=exited
docker ps --filter name=web
docker ps --filter ancestor=nginx:alpine
docker ps --filter label=env=production

# Custom format with Go templates
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'

# Show file sizes (writable layer + image)
docker ps -s

docker ps is the inventory command. By default it only shows running containers; add -a to see everything including exited ones — that's how you find the container that died ten minutes ago so you can docker logs it.

The default columns are container ID, image, command, age, status, ports, and name. --format lets you pull just the columns you want; useful in scripts.

Stop a container — docker stop

bash
docker stop CONTAINER_NAME

# Custom grace period (default is 10s)
docker stop -t 30 CONTAINER_NAME

# Stop all running containers
docker stop $(docker ps -q)

docker stop sends SIGTERM to the container's main process, waits 10 seconds for it to exit gracefully, then sends SIGKILL if it hasn't. The 10-second wait is the most common reason a docker stop "feels slow" — your app isn't reacting to SIGTERM, so Docker waits the full timeout before killing.

Two ways to fix that:

  • Use exec form for CMD/ENTRYPOINT in your Dockerfile. Shell form (CMD npm start) wraps your process in /bin/sh -c, which receives SIGTERM but doesn't pass it to your app. Exec form (CMD ["npm", "start"] or CMD ["node", "server.js"]) sends signals directly. See How to Write a Dockerfile.
  • Handle SIGTERM in your app. Express, Fastify, FastAPI etc. all have graceful-shutdown patterns. Without one, your app gets killed mid-request.

docker kill CONTAINER_NAME skips the SIGTERM and sends SIGKILL immediately. Useful when an app is wedged and stop is taking too long.

Start and restart a stopped container — docker start, docker restart

bash
# Start a stopped container (keeps the same name, same volumes, same env)
docker start CONTAINER_NAME

# Start AND attach my terminal to it
docker start -a CONTAINER_NAME

# Restart (stop + start)
docker restart CONTAINER_NAME

# Restart with custom grace period
docker restart -t 30 CONTAINER_NAME

A stopped container is not gone. docker start brings it back exactly as it was — same name, same network attachments, same volumes mounted, same env. This is the difference between docker start and docker run: run always creates a new container; start resurrects an existing one.

When to use which:

  • docker restart when a service is misbehaving and you want to reset it without losing its config. Database isn't responding to queries; bounce it.
  • docker start when you previously stopped a container and want it back. Common after host reboots if you don't have a --restart policy set.
  • docker run when you want a fresh container, possibly with different flags.

Restart policies — surviving crashes and reboots

If you want a container to come back automatically after a crash or a host reboot, set a restart policy:

bash
docker run -d --restart unless-stopped CONTAINER_NAME IMAGE_NAME
PolicyBehavior
no (default)Never restart.
on-failure[:max]Restart only on non-zero exit. Optional max-attempts.
alwaysRestart, including after host reboot. Restarts even if you manually stopped it.
unless-stoppedRestart, including after host reboot. Does NOT restart if you stopped it manually.

unless-stopped is the right default for almost every long-running service. Full breakdown plus health-check pairing in Docker Restart Policies and Health Checks.

Pause and unpause — docker pause

bash
docker pause CONTAINER_NAME
docker unpause CONTAINER_NAME

pause uses the cgroups freezer to suspend every process inside the container. It is not the same as stop — the processes are still there, they just aren't scheduled. Memory is preserved. Useful for snapshotting / quiescing state without a full restart.

Not commonly needed in day-to-day work. The two cases I've actually used it: pausing a CPU-hungry container to let another finish first, and pausing a database briefly while taking a filesystem snapshot of its volume.

Rename a container — docker rename

bash
docker rename OLD_NAME NEW_NAME

Just changes the name. The container ID, volumes, network attachments, everything else stays the same. Useful when you started with --name old_temp and want to give it a real name without recreating.

Remove a container — docker rm

bash
# Remove a stopped container
docker rm CONTAINER_NAME

# Force-remove (stops it first if running)
docker rm -f CONTAINER_NAME

# Remove a container AND its anonymous volumes
docker rm -v CONTAINER_NAME

# Remove all stopped containers in one go
docker container prune

# Remove ALL containers (running and stopped) — destructive
docker rm -f $(docker ps -aq)

docker rm deletes the container's filesystem (the writable layer) and metadata. The image is unaffected; you can docker run again from the same image and get a fresh container.

By default, named volumes survive docker rm — that's the whole point of using a named volume. Anonymous volumes (volumes you didn't name, declared by VOLUME in a Dockerfile or just -v /path) survive by default too, but -v (or --volumes) on docker rm removes them. To clean them up later, docker volume prune.

The --rm flag — auto-remove on exit

bash
# Container is deleted the instant its main process exits
docker run --rm IMAGE_NAME

# --rm + -it = the throwaway interactive shell pattern
docker run --rm -it IMAGE_NAME bash

--rm is for one-shot containers — try a tool, run a quick command, get an interactive shell to poke around. Without --rm, every quick test leaves a stopped container behind, and a week later docker ps -a is a graveyard.

Do NOT use --rm on services you might want to inspect later. Once the container exits, docker logs is no longer available for it.

Common patterns

Find and stop a container by name pattern:

bash
docker stop $(docker ps -q --filter name=web)

Stop everything cleanly:

bash
docker stop $(docker ps -q)

Clean up all stopped containers:

bash
docker container prune
# or, with no prompt:
docker container prune -f

Restart a container that's stuck:

bash
docker kill CONTAINER_NAME && docker start CONTAINER_NAME

Watch a container's lifecycle events live:

bash
docker events --filter type=container --filter container=CONTAINER_NAME

Useful when debugging a container that keeps restarting — the events show every start, die, restart with timestamps and exit codes.

Common pitfalls

  • docker stop taking 10 full seconds every time. Your app isn't handling SIGTERM. Either use exec form in your Dockerfile's CMD, or add a SIGTERM handler in the app. See How to Write a Dockerfile.
  • "Conflict. The container name '/web' is already in use." A container with that name still exists, even if stopped. Either docker rm web first, or docker run --rm to auto-clean, or use a different name.
  • docker ps doesn't show my container — but docker ps -a does. It's not running. Either it crashed (check docker logs) or it exited normally (single-shot command). Restart with docker start.
  • Containers piling up over time. Add --rm to one-shot runs, and periodically docker container prune. Or use a restart policy and let Docker manage state.
  • docker rm doesn't delete the volume. Correct — named and anonymous volumes survive container removal by default. Use docker rm -v to remove anonymous volumes, and docker volume prune to clean up unused ones.

FAQ

Sources

Authoritative references this article was fact-checked against.

TagsDockerdocker psdocker stopdocker rmContainersDevOps

Found this useful? Pass it on.

Copied
Ishan Karunaratne

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

R

Running Docker Containers as a Non-Root User

By default, processes inside Docker containers run as root, which is risky. Switch to a non-root USER, fix permissions on volumes and ports, and configure Compose and Kubernetes to refuse to run root containers.

d

docker logs: View Container Output and Tail Logs

Read the stdout and stderr of a running or stopped container. Follow live output, tail the last N lines, filter by time, prepend timestamps, and the cases where docker logs doesn't help because the app writes to a file instead.