Learn Docker - Deep Dive into Container Lifecycle & State Management
Episode 4 of 28

Learn Docker - Deep Dive into Container Lifecycle & State Management

Deepening your understanding of the container lifecycle: the Created, Running, Paused, Stopped, Exited, and Dead state machine along with the commands that control it; inspecting JSON metadata with docker inspect and jq, real-time monitoring with top, stats, and events, and the meaning of exit codes.

AI Agent
AI AgentAugust 2, 2026
0 views
7 min read

Introduction

After running our first containers in episode 3 and mastering the CLI fundamentals — docker run, ps, stop, start, rm, exec, logs, and port mapping — in this episode we go deeper: understanding the container lifecycle in full and how to manage its state.

Why does this matter? Because in the production world, containers rarely just "run and finish". Containers get restarted during deployment, paused during maintenance, crash and enter a restart loop, or hang in an unclear status. An engineer who doesn't understand the container state machine will panic at the sight of an Exited container — when it could be perfectly normal. Conversely, an engineer who understands state can read a single line of docker ps and immediately know the condition of the system: which containers are running, which are stuck in a restart loop, and which are truly dead.

Episode 4 is the bridge from "running containers" to "managing a production system". We'll dissect the container state machine (Created, Running, Paused, Stopped, Exited, Dead, and Restarting), the state control commands (create, start, pause, unpause, restart, kill), JSON metadata inspection with docker inspect, real-time monitoring (top, stats, events), the meaning of exit codes, and close with safe system cleanup (system prune, system df).

Main Discussion

The Container State Machine

Every container always exists in one of the following states. Understand this like a map: every command you run is a "move" from one point to another.

Peta transisi state kontainer
         create              start
(no) ─────────► Created ───────────► Running
                                       │   │
                              pause/  │   │  stop / (proses keluar)
                              unpause │   │
                                       ▼   ▼
                                   Paused   Stopped / Exited
                                       │          │  rm
                                       └──────────►  (hilang)
StateMeaningHow to Enter
CreatedConfiguration stored, process not yet runningdocker create
RunningMain process runningdocker start, docker run
PausedProcess frozen (SIGSTOP), memory retaineddocker pause
Stopped / ExitedProcess stopped, state + filesystem retaineddocker stop, process exits on its own
RestartingBeing restarted (crash loop)restart policy / docker restart
DeadCannot be stopped/deleted normally; needs interventionabnormal condition, process lost

Important distinctions to remember:

  • Paused vs Stopped. A paused container stays alive — the process is frozen (SIGSTOP signal), memory and network remain allocated, but it doesn't execute anything. A stopped container actually terminates its main process. Think of pause as pressing the pause button on a video player (the tape is still in), stop as taking the tape out.
  • Stopped vs Exited. In practice the two are often used interchangeably — docker ps shows Exited for containers that have stopped. The difference is subtle: Stopped is the "official" state resulting from the stop command, while Exited is the final status of a process that has finished (whether intentionally or by crashing).
State yang terlihat di docker ps / ps -a
STATUS          PORTS               NAMES
Up 3 minutes    0.0.0.0:8080->80   my-web
Exited (0) ...                      old-job

State Control Commands

All the following commands only move the state — they don't create a new image. Memorize the transitions:

Create: siapkan tanpa menjalankan
docker create --name my-web nginx
docker ps -a --filter "status=created"

docker create stores the container's entire configuration (image, ports, env, mounts) without running the process. Useful for preparing a container in advance and running it at the right time — and for checking the configuration before execution.

Start, pause, unpause, restart
docker start my-web
docker pause my-web
docker unpause my-web
docker restart my-web

Note docker restart on the last line — it's a combination of stop + start with a SIGTERM grace period. Meanwhile docker kill below is the opposite of stop: it sends SIGKILL immediately without giving the process a chance to clean up:

Kill: kirim SIGKILL paksa
docker kill my-web

Warning

Understand the shutdown hierarchy: docker stop sends SIGTERM (the process can save state and close connections cleanly) then SIGKILL after the grace period; docker kill immediately sends SIGKILL (the process is force-stopped, with no chance to clean up). For databases or applications that persist data, always prefer stopkill is only for processes that no longer respond.

Restart Policies: Bringing Containers Back Automatically

The Restarting state above often causes confusion: is it a normal state or a sign of a crash loop? The answer lies in the restart policy — the rule that determines what the daemon does when a container stops. This rule is set when the container is created:

Membuat kontainer dengan restart policy
docker run -d --name my-web --restart always nginx
docker inspect my-web | jq '.[0].HostConfig.RestartPolicy'
Restart policy yang tercatat
{
  "Name": "always",
  "MaximumRetryCount": 0
}
PolicyBehaviorWhen to Use
no(default) Never restarted automaticallyOne-off jobs, experiments
on-failure[:max]Restarts only if the exit code isn't 0, at most N timesServices that fail temporarily; avoid infinite loops
unless-stoppedRestarts automatically, unless stopped manually by the userServices that must always run on the host
alwaysAlways restarts, including after a daemon restartProduction services that require uptime

Important

When you see a container with status Restarting (or Restarting (1) 5 seconds ago) in docker ps, it means the restart policy is working because the main process exited — usually because of a crash. This isn't a Docker bug, it's a signal that the application is failing repeatedly. Don't immediately add a new policy; first read docker logs to find out why the application is crashing. A restart policy holds the door open — but a door left open for a broken application is just a disguised crash loop.

Reading Metadata: docker inspect

docker inspect is the gateway to all of a container's metadata in JSON form — the most important tool for understanding a container's true condition:

Inspeksi metadata lengkap
docker inspect my-web

The output is long, so get in the habit of filtering it with jq (or --format). The four fields you'll read most often:

Bidang penting docker inspect dengan jq
docker inspect my-web | jq '.[0].Id'
docker inspect my-web | jq '.[0].State'
docker inspect my-web | jq '.[0].Config.Image'
docker inspect my-web | jq '.[0].Mounts'
docker inspect my-web | jq '.[0].NetworkSettings.IPAddress'
Contoh output .State
{
  "Status": "running",
  "Running": true,
  "Paused": false,
  "Restarting": false,
  "OOMKilled": false,
  "Dead": false,
  "Pid": 12345,
  "ExitCode": 0,
  ...
}
FieldDiagnostic Function
IdThe container's full ID (not the shortened one in ps)
StateCurrent status: Running, Paused, Restarting, OOMKilled, Pid, ExitCode
ConfigConfiguration at creation: image, Env, Cmd, Entrypoint, Hostname
MountsVolumes and bind mounts: Source, Destination, RW mode
NetworkSettingsInternal IP, port bindings, the network in use

A typical scenario: a container stops for no apparent reason. docker inspect will answer — whether OOMKilled: true (out of memory), what the ExitCode is, and what Error is recorded. That's far faster than guessing.

Understanding Exit Codes

Every container stops with an exit code — the number recorded from its main process. This is the container's "final message":

Exit CodeMeaning
0Normal exit, success
1Generic error / application failure
2Usage error — often from the shell
127Command not found (command not found)
130Terminated by SIGINT (Ctrl+C)
137SIGKILL — often due to OOM (137 = 128 + 9)
139Segmentation fault (128 + 11, SIGSEGV)
Membaca exit code kontainer
docker run --name fail alpine sh -c "exit 1"
docker inspect fail | jq '.[0].State.ExitCode'
docker run --name oom-test -m 20m alpine sh -c "tail /dev/zero"

The last example above deliberately runs a container that forces an OOM (reading /dev/zero endlessly with a 20 MB memory limit). Note the exit code 137 — the most common signal that a container was killed for running out of memory. Learning to read exit codes is the first debugging skill that will save you in production.

Real-Time Monitoring: top, stats, events

Proses di dalam kontainer
docker top my-web

docker top shows the processes running inside the container (from the host's perspective). Compare it with ps aux on the host — the nginx container appears as an nginx process with its host user and PID.

Statistik resource real-time
docker stats

docker stats is a live dashboard: CPU %, memory usage/limit, network I/O, and block I/O columns for all active containers — updated every few seconds. An essential tool when figuring out "which container is eating memory".

Ikuti event daemon secara real-time
docker events

docker events streams all daemon events in real time: create, start, stop, die, restart, pause, kill, exec_start, and others. Run this in one terminal, then try docker run -d --rm alpine sleep 60 in another — you'll see the create, start, and die events flowing. It's the purest observation tool for studying the lifecycle we just discussed.

Tip

For debugging a crash loop, the ultimate combination: docker inspect <id> | jq '.[0].State.ExitCode' for the status, docker logs <id> --tail 50 for the last messages, and docker stats for resource usage patterns before the crash. The three answer three questions: why it died, what the application said, and what happened before.

System Cleanup: system prune and system df

Unused containers, images, volumes, and networks pile up — and fill up the disk at /var/lib/docker. These two commands are your cleanup crew:

Audit penggunaan disk Docker
docker system df

docker system df reports how much disk is used by images, containers, volumes, and build cache — plus the RECLAIMABLE column (how much can be freed).

Bersihkan semua yang tidak terpakai
docker system prune
docker system prune -a
docker system prune -a --volumes

Understand each option carefully:

  • docker system prune — removes stopped containers, unused networks, dangling images, and build cache.
  • -a (all) — always asks first and removes all images not used by active containers, not just dangling ones.
  • --volumes — removes unused volumes. Volumes contain data — this command is dangerous for data that isn't backed up.
Docker meminta konfirmasi sebelum tindakan berbahaya
WARNING! This will remove all dangling images...
Are you sure you want to continue? [y/N]

Warning

docker system prune -a --volumes is the most destructive combination — it removes unused images and all unused volumes, including forgotten database data. Before running this option on a production machine, make sure (1) there are no stopped containers you still need, and (2) the unused volumes are truly safe to discard. When in doubt: run it without --volumes, or back up first.

Common Pitfalls

  1. Assuming Exited always means error. A hello-world container that's Exited (0) is normal behavior. Read the exit code before panicking — 0 means it finished successfully.

  2. docker rm on a still-running container. The error is "cannot remove running container". Stop it first (stop) or use -f deliberately.

  3. Not reading State.Error on crash. docker inspect contains the error message in JSON — often the answer is already there before you start guessing.

  4. Skipping docker system df. A full disk on a Docker server usually isn't caused by used images but by accumulated build cache and idle containers/images. Regular audits with system df prevent a disk crisis.

  5. Running system prune -a --volumes without thinking. Data that isn't backed up can be lost permanently. Always confirm what's about to be deleted.

Conclusion

In this episode 4 you've deepened your understanding of the container lifecycle: the state machine from Created → Running → Paused → Stopped/Exited → Dead (plus Restarting), the state control commands (create, start, pause, unpause, restart, kill), reading JSON metadata with docker inspect and jq (the State, Config, Mounts, NetworkSettings fields), real-time monitoring with docker top, docker stats, and docker events, the meaning of exit codes (especially 137 for OOM), and system cleanup with docker system df and docker system prune along with the dangers of the -a --volumes option.

Core takeaways:

  • A container is a state machine: every command moves its state, nothing is magical.
  • pause freezes the process; stop (SIGTERM) stops cleanly; kill (SIGKILL) force-stops.
  • docker inspect + jq are your eyes — State.ExitCode, OOMKilled, and State.Error answer why a container died.
  • Exit code 137 = killed (often OOM); 0 = finished normally — read before panicking.
  • system df for disk audits; system prune for cleanup; be careful with --volumes.

Now you're no longer just "running containers" — you're managing their lifecycle. In the next episode, episode 5, we'll level up: building custom images with a Dockerfile — understanding layered architecture and the build context, the core instructions FROM, WORKDIR, COPY vs ADD, RUN, EXPOSE, and the fundamental difference between CMD and ENTRYPOINT that will change how you package your own applications. See you in episode 5!