skip to content

After a container stops, where do you find the exit code it reported, and what do the values 0 and 1 tell you about what happened?

level: juniorimportance: must knowfreq 62%

answer

  1. Container = PID 1; PID 1 exits, container exits
  2. `docker ps -a` STATUS → `Exited (N)`
  3. `docker inspect --format '{{.State.ExitCode}}'`
  4. 0 = success; 0 on a server = suspicious
  5. 1 = generic app error → read `docker logs`

basics

~20 s

docker ps -a shows Exited (N) in the STATUS column, and docker inspect --format '{{.State.ExitCode}}' <container> gives the number. 0 means the main process finished successfully; any non-zero value means it failed — 1 is the generic "the application errored" code.

solid answer

~50 s

A container's exit code is the exit status of its **main process (PID 1)**. When that process ends, the container ends, and the code is recorded. Two places to read it: - `docker ps -a` → STATUS column shows `Exited (1) 30 seconds ago`. - `docker inspect --format '{{.State.ExitCode}}' <container>` → just the number (also `.State.Error`, `.State.OOMKilled`, `.State.FinishedAt`). `0` = the process completed successfully; for a long-running server, exiting 0 is still suspicious because it means it decided to stop. `1` is the generic application-error code — a config parse failure, an unhandled exception, an explicit `exit 1` in an entrypoint script. Non-zero says "something went wrong" but not what, so the code is only step one: pair it with `docker logs <container>` for the actual message. Codes in the 125–127 and 128+ ranges are special and have specific meanings beyond "the app failed".

go deeper

for a junior

Know that the container's code is PID 1's code, that docker ps -a and docker inspect show it, and that 0 = success, non-zero = failure.

for a middle

Add why a server exiting 0 is a bug (daemonizing/foreground process), and that 1 is generic so logs are the real diagnosis.

for a senior

Frame the exit code as the input to restart policies and CI gating, and mention docker wait plus .State.Error/.State.FinishedAt when scripting or triaging.

for a principal

Talk about making exit codes meaningful as a platform contract — services that exit non-zero on unrecoverable config errors, never daemonize, and never exit 0 on failure, so restart and alerting policy can be uniform.

## What an exit code is Every process on Linux ends with an 8-bit **exit status**, a number from 0 to 255 that it hands back to whoever started it. By universal convention `0` means success and anything non-zero means failure. In a shell you read it as `$?`. A container is a wrapper around one **main process** — PID 1 inside the container's namespace, the thing your `ENTRYPOINT`/`CMD` launched. The container's lifetime is that process's lifetime: when PID 1 exits, the container transitions to the `exited` state regardless of whether other processes are still alive inside it. The exit status of that process becomes the container's exit code, and the daemon stores it in the container's state record. ## Where to read it A stopped container still exists (with its filesystem, logs, and state) until you `docker rm` it, so the code survives the process. - `docker ps -a` lists stopped containers too; the STATUS column reads `Exited (1) 2 minutes ago`. This is the quickest look. - `docker inspect --format '{{.State.ExitCode}}' <container>` prints only the number, which is what you want in scripts. - The whole state block is worth knowing: `docker inspect --format '{{json .State}}' <container>` gives `Status`, `ExitCode`, `Error`, `OOMKilled`, `StartedAt`, `FinishedAt`. `Error` often carries the daemon's own message when the container never really started. - `docker wait <container>` blocks until the container stops and then prints its exit code — handy in CI scripts. - `docker run` in the foreground (without `-d`) passes the container's exit code through as the exit code of the `docker run` command itself, so `echo $?` after it works exactly as you'd expect. ## Reading 0 `0` means the main process ran to completion and reported success. For a batch job — a migration, a test run, a one-shot CLI — that is the outcome you wanted, and it is why CI pipelines gate on `docker run ... && next-step`. For a **long-running service**, exit 0 is not automatically good news. It means the server decided to stop on its own: it finished reading stdin and quit, it forked into the background and the foreground process returned (a classic mistake — the daemon "started" and PID 1 immediately exited), or it received a shutdown signal and its handler chose to `exit(0)`. A web server that shows `Exited (0)` seconds after launch is almost always a startup bug, not a clean run. ## Reading 1 `1` is the catch-all failure code. Language runtimes use it for an uncaught exception; shells use it for a failed command; most programs use it for "bad configuration, cannot continue". Concretely you'll see it when an environment variable is missing, a config file fails to parse, a required port is already bound, a database connection is refused at boot, or an entrypoint script hits `set -e` and a command failed. Because `1` is generic, it tells you *that* the application failed, never *why*. The next command is always `docker logs <container>` (add `--tail 50` on a noisy container). Applications that want to be more informative define their own codes — anything from 2 to 124 is fair game for an application to use for its own meanings, and some tools do (curl, for instance, has dozens). ## Why the code matters operationally The exit code is the input to everything downstream: - **Restart behaviour**: `--restart=on-failure` restarts only for non-zero exits, so a process that exits 0 by mistake will never be restarted. - **CI/CD**: a build step's pass/fail is literally the container's exit code. - **Orchestrators** surface the same number when they report a crash, so the vocabulary transfers. ## Ranges to be aware of Docker reserves some values so it can report failures that are the *daemon's* problem rather than the app's: `125` (the `docker run` invocation itself failed), `126` (the command was found but is not executable), `127` (the command was not found). And any container killed by a signal reports `128 + signal number` — `137` for SIGKILL (9), `143` for SIGTERM (15). So when a code looks strange, check whether it falls in one of those buckets before blaming application logic.

  • Your web server container shows `Exited (0)` two seconds after `docker run -d`. What is the likely cause?
    The main process almost certainly daemonized or self-terminated: it forked a background worker and the foreground process returned success, or it ran a command that completed rather than a server that blocks. Containers need a foreground process — start the server with its no-daemon/foreground flag (for example `nginx -g 'daemon off;'`). Check `docker logs` to confirm it printed a normal startup-then-exit sequence rather than an error.
  • Does a non-zero exit code delete the container or its logs?
    No. A stopped container keeps its writable layer, its configuration, and its log file until you run `docker rm` (or started it with `--rm`, which removes it as soon as it exits). That is why you can still run `docker logs` and `docker inspect` on an exited container — and why using `--rm` on a crashing container makes debugging much harder.

saying these in an interview costs you the question

  • Thinking the exit code belongs to the whole container's process tree rather than to PID 1 specifically
  • Assuming exit 0 always means healthy, even for a server that stopped seconds after start
  • Treating exit code 1 as diagnostic in itself instead of immediately reading the logs
  • Running crashing containers with `--rm` and then wondering why there is nothing left to inspect

context