Which `docker inspect` fields explain why a container exited, and how do you print just them?
answer
- Engine-side truth, not application output
- One object holds the post-mortem
- A boolean settles the memory question
- Two timestamps classify startup versus runtime
- One count lives outside State
basics
~10 sRead .State.ExitCode, .State.OOMKilled, .State.Error, .State.StartedAt and .State.FinishedAt, plus the top-level .RestartCount — RestartCount sits outside State. Print them with docker inspect --format and a Go template instead of paging the whole JSON.
solid answer
~40 s`docker inspect <container>` returns the container's full JSON, and five fields carry the post-mortem. `.State.ExitCode` is the status the main process returned. `.State.OOMKilled` is a boolean that is the only definitive evidence the kernel's out-of-memory killer took the process. `.State.Error` holds the runtime's own message when the container failed to start at all. `.State.StartedAt` and `.State.FinishedAt` bound the lifetime — milliseconds means it died during startup, minutes means it got past it. `.RestartCount` is a **top-level** field, not part of `State`, and counts how many times a restart policy has put the container back. Print them with `docker inspect --format '{{.State.ExitCode}} {{.State.OOMKilled}} {{.RestartCount}}' <container>` rather than scrolling several hundred lines of JSON.
code
bash · 3 linesdocker inspect --format \
'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} restarts={{.RestartCount}} started={{.State.StartedAt}} finished={{.State.FinishedAt}}' \
pdfsign-apigo deeper
Know that docker inspect <container> exists and returns JSON, and that --format with a Go template prints a single field. Being able to fetch the exit code without scrolling the whole document is the bar here.
Name the fields precisely and say what each one proves: ExitCode, OOMKilled, Error, StartedAt/FinishedAt, and the top-level RestartCount. Explain why the lifetime between the two timestamps separates a startup failure from a runtime failure.
Combine the fields into a verdict under time pressure, and know their limits — OOMKilled can miss a killed child process, and State describes only the most recent run of a restarting container.
Argue for making this evidence automatic rather than manual: exit status, OOM flags and restart counts collected off the host and alerted on, so nobody has to be logged in to a specific machine to learn that a service has died 47 times.
`docker logs` tells you what the application said; `docker inspect` tells you what the engine observed. When the logs are empty, truncated or unhelpful, the inspect record is the structured evidence that settles the question of *how* the process ended. ## The container JSON, and where State stops `docker inspect <container>` prints a one-element JSON array describing the container. The interesting object for triage is `State`, but two of the fields people expect to find there are elsewhere, and getting that wrong is a common interview stumble: - `State` holds `Status`, `Running`, `Paused`, `Restarting`, `OOMKilled`, `Dead`, `Pid`, `ExitCode`, `Error`, `StartedAt`, `FinishedAt` (and `Health`, when the image or run declares a health check). - `RestartCount` is a **top-level** field on the container object, a sibling of `State`, not a member of it. - The restart *policy* that produced those restarts lives under `HostConfig.RestartPolicy`, again outside `State`. - The command the daemon actually resolved to run is in the top-level `Path` and `Args`, which is how you confirm that the process being executed is the one you think it is. ## Reading the five fields **`.State.ExitCode`** is the status the container's main process returned, or the signal-derived status when it was killed. It narrows the space quickly but rarely closes the case on its own; the specific meanings of the common codes are a topic of their own, and the right instinct is to treat the code as a pointer, not a verdict. **`.State.OOMKilled`** is a boolean and it is the field that matters most in a memory incident, because it is *engine-observed*: the daemon saw an out-of-memory event for the container's cgroup. A true value means the kernel killed the process for exceeding memory available to it — either the container's own limit or the host's. Two caveats are worth knowing. First, it can read `false` while the OOM killer was still involved, because if a *child* process inside the container is killed and the main process then exits with its own status, the daemon may not attribute an OOM to the container; correlate with the host's kernel log when the exit looks memory-shaped. Second, `true` does not by itself say whether the limit was too small or the application leaked — that judgement needs usage history. **`.State.Error`** carries the runtime's message from the last start attempt: the text you also saw on `docker run` when the container never got going, typically an OCI runtime create failure naming a binary or path that could not be executed. It is empty for a container that started successfully and later exited — which makes an empty `Error` plus a non-zero `ExitCode` a clean signal that the application, not the engine, made the decision. **`.State.StartedAt` / `.State.FinishedAt`** are RFC3339 timestamps. Subtracting them is the cheapest classifier in container triage: a lifetime measured in tens of milliseconds means the process never completed startup (missing config, unreadable secret, bad argument), whereas one measured in minutes means startup succeeded and something later — a request, a timer, a memory ceiling — ended it. `FinishedAt` on a never-started container is the zero timestamp `0001-01-01T00:00:00Z`, which is itself a tell. **`.RestartCount`** answers "is this one death or a loop?". A count of 47 on a container currently reporting `Up 6 seconds` means you are looking at the latest of many identical failures, and that `State` describes only the most recent one. ## Printing just what you need The raw JSON runs to hundreds of lines, so use the Go template that `--format` accepts: ``` docker inspect --format '{{.State.Status}} {{.State.ExitCode}} {{.State.OOMKilled}} {{.RestartCount}}' pdfsign-api ``` For several containers at once, `docker inspect` takes multiple arguments and applies the template to each, so `docker ps -aq | xargs docker inspect --format '{{.Name}} {{.State.ExitCode}} {{.State.OOMKilled}}'` sweeps a whole host. When you would rather post-process, `docker inspect` output is ordinary JSON and pipes cleanly into a JSON processor. Both beat eyeballing the full document. ## A worked example A PDF-signing service running a Django application under gunicorn is restarted by its policy repeatedly. `docker inspect --format '{{.State.ExitCode}} {{.State.OOMKilled}} {{.RestartCount}} {{.State.StartedAt}} {{.State.FinishedAt}}' pdfsign-api` returns `137 true 47 2026-08-19T02:11:43.7Z 2026-08-19T02:14:02.1Z`. Three facts fall out at once: the kernel killed it, it survived about 139 seconds each time rather than dying at startup, and it has done so 47 times. That combination — healthy startup, death after a couple of minutes under load, OOMKilled true — points at memory growth while serving traffic rather than a misconfiguration, and tells you which artefact to gather next: memory usage over the lifetime of one run. ## Why this is the field set to memorise Because it is engine-side truth. Application logs can be missing, buffered, rotated away or actively lying; `State` is what the daemon recorded about the process it supervised. Learning the exact names — and remembering that `RestartCount` lives outside `State` — is the difference between a confident two-command diagnosis and a fishing expedition.
- Can `.State.OOMKilled` be false even though the kernel's OOM killer was involved?Yes. The flag records an out-of-memory event the daemon attributed to the container's main process. If the OOM killer picks a *child* process — a worker rather than the supervisor — the main process may then exit with its own status and the flag can read false. When an exit looks memory-shaped but the flag is false, correlate with the host's kernel log for the OOM killer's own message naming the victim.
- What does `.State.Error` contain, and when is it empty?It holds the runtime's message from the last start attempt — typically an OCI runtime create failure naming a binary or mount path that could not be used. It is empty for any container that started successfully, so an empty `Error` alongside a non-zero `ExitCode` tells you the engine did its job and the application chose to exit.
- How would you get these fields for every container on a host in one command?`docker inspect` accepts many container IDs and applies the `--format` template to each, so `docker ps -aq | xargs docker inspect --format '{{.Name}} {{.State.ExitCode}} {{.State.OOMKilled}} {{.RestartCount}}'` prints one line per container. Add `--filter status=exited` to the `docker ps` call when you only care about the dead ones.
saying these in an interview costs you the question
- Looks for RestartCount inside State
- Treats the exit code alone as a complete diagnosis
- Believes OOMKilled true always means the container limit was too low
- Pages the whole inspect JSON instead of using --format
- Confuses State.Error with the application's stderr output
- Thinks inspect keeps a history of previous runs