Suggest an editImprove this articleRefine the answer for “How to inspect container state, logs, and metadata?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Three commands cover almost everything.** `docker ps -a` shows status. `docker logs` shows stdout/stderr. `docker inspect` shows full metadata as JSON. ```bash docker ps -a --filter name=web # status, ports, names docker logs -f --tail 100 web # follow last 100 lines docker inspect web # full JSON metadata docker inspect --format '{{.State.Status}}' web docker stats --no-stream web # CPU, memory, I/O snapshot ``` **Key:** `inspect` is the source of truth for everything Docker knows. `logs` and `stats` are specific views. Use `--format` with `inspect` to extract specific fields without grep/jq.Shown above the full answer for quick recall.Answer (EN)Image**Three commands cover the entire observability story** for a single container: `docker ps` for high-level state, `docker logs` for what the app printed, `docker inspect` for everything else. Plus `docker stats` for live resource use. ## Theory ### TL;DR - `docker ps -a` = current state, ports, image, name (one-line summary). - `docker logs <name>` = container's stdout + stderr. `-f` to follow live, `--tail N` for last N lines, `--since 10m` for time-bounded. - `docker inspect <name>` = full JSON of the container's config, state, networks, volumes, mounts, env. Use `--format` to extract one field. - `docker stats` = live CPU, memory, network, block I/O. `--no-stream` for a single snapshot. - `docker top <name>` = process tree inside the container. - `docker port <name>` = which host ports map to which container ports. ### `docker ps` — quick state ```bash $ docker ps -a --filter name=web CONTAINER ID IMAGE STATUS PORTS NAMES a3f9d2b8c1e4 nginx:1.27-alpine Up 5 minutes 0.0.0.0:8080->80/tcp web ``` Four columns answer most questions: *is it running? what image? since when? which ports?* ### `docker logs` — what the app said The daemon captures stdout and stderr from PID 1 inside the container. `docker logs` reads it back. ```bash docker logs web # all logs since start (can be huge) docker logs -f web # follow live (like tail -f) docker logs --tail 100 web # last 100 lines docker logs --since 10m web # last 10 minutes docker logs --since 2026-04-30T10:00:00 web # since timestamp docker logs -t web # prepend timestamps ``` **Important:** logs come from PID 1's stdout/stderr. If your app writes to a file inside the container, `docker logs` will not show it. Configure your app to log to stdout for this to work — that is the 12-factor norm. ### `docker inspect` — everything else Full JSON dump of the container's state and config. Without `--format`, it is a 200-line JSON document. With `--format`, you get exactly one field: ```bash # Status docker inspect --format '{{.State.Status}}' web # → running # Exit code (after the container exits) docker inspect --format '{{.State.ExitCode}}' web # → 137 # Was it OOM-killed? docker inspect --format '{{.State.OOMKilled}}' web # → true / false # Restart count docker inspect --format '{{.RestartCount}}' web # → 3 # IP address on the default bridge docker inspect --format '{{.NetworkSettings.IPAddress}}' web # → 172.17.0.2 # All published ports docker inspect --format '{{json .NetworkSettings.Ports}}' web # → {"80/tcp":[{"HostIp":"0.0.0.0","HostPort":"8080"}]} # Mounts docker inspect --format '{{range .Mounts}}{{.Type}}: {{.Source}} -> {{.Destination}}\n{{end}}' web ``` The `--format` template uses Go's `text/template` syntax. The output of `docker inspect <name>` (without format) shows the field structure you can target. ### `docker stats` — live resources ```bash $ docker stats --no-stream CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS a3f9d2b8c1e4 web 0.01% 12.5MiB / 7.8GiB 0.16% 2.1kB / 832B 0B / 0B 5 b7e1f4d6a2b8 db 2.34% 145MiB / 7.8GiB 1.82% 18.4kB / 12.7kB 4.1MB / 0B 9 ``` Without `--no-stream`, it refreshes live like `top`. Useful for spotting memory leaks (steadily-rising MEM USAGE) or noisy neighbors. ### `docker top` — process tree inside ```bash $ docker top web UID PID COMMAND root 84231 nginx: master process nginx -g daemon off; nginx 84290 nginx: worker process ``` Host-side view: PIDs are host PIDs, not container's PID 1 numbering. Useful for matching `ps aux | grep` on the host with what is inside. ### `docker port` — port mappings ```bash $ docker port web 80/tcp -> 0.0.0.0:8080 80/tcp -> [::]:8080 ``` Quick way to see what is exposed without parsing inspect output. ### Common mistakes **Looking in the writable layer for logs** ```bash # WRONG: app logs to /var/log/myapp.log inside the container $ docker exec web cat /var/log/myapp.log # RIGHT: configure the app to log to stdout, then $ docker logs web ``` Apps that log to files inside the container do not show up in `docker logs`. They are also lost on `docker rm`. Always log to stdout/stderr for containerized apps. **Forgetting `--no-stream` in scripts** ```bash # Hangs forever in a script (waits for live updates) $ docker stats > stats.txt # Snapshot, exit, return $ docker stats --no-stream > stats.txt ``` `docker stats` defaults to streaming. In a CI or scripted context, always add `--no-stream`. **Using `docker inspect` without `--format` in scripts** ```bash # Verbose, hard to parse $ docker inspect web | grep IPAddress | head -1 | awk -F'"' '{print $4}' # Clean $ docker inspect --format '{{.NetworkSettings.IPAddress}}' web ``` The `--format` flag is exactly to skip the grep/awk dance. **Reading old logs without `--tail`** A long-running container can have gigabytes of logs. `docker logs <name>` (no flags) tries to dump them all. Use `--tail 1000` or `--since` to bound the output. ### Real-world usage - **"Is service X up?"** → `docker ps --filter name=X --filter status=running -q`. Empty output = not running. - **"Why did the build container exit?"** → `docker logs --tail 200 build-container`. - **"What IP is the database on?"** → `docker inspect --format '{{.NetworkSettings.Networks.bridge.IPAddress}}' db`. - **"How much memory is web using right now?"** → `docker stats --no-stream web`. - **"Was that crash an OOM?"** → `docker inspect --format '{{.State.OOMKilled}}' container`. ### Follow-up questions **Q:** How is `docker logs` different from `docker logs -f`? **A:** Without `-f`, it dumps logs accumulated so far and exits. With `-f` (follow), it streams new logs as they appear, like `tail -f`. Press Ctrl+C to stop following. **Q:** Can I see logs from before the container restarted? **A:** Yes — `docker logs` retains logs across restarts (until `docker rm`). The default driver (`json-file`) appends to a log file on disk that survives restart. Other drivers (syslog, journald, fluentd) ship logs externally, in which case `docker logs` may not work — check `docker inspect --format '{{.HostConfig.LogConfig.Type}}'`. **Q:** What is the difference between `docker inspect` and `docker container inspect`? **A:** `docker inspect` is generic — works on containers, images, volumes, networks. `docker container inspect` is the explicit form for containers only. Identical output for containers. **Q:** How do I see resource limits applied to a container? **A:** `docker inspect --format '{{.HostConfig.Memory}} {{.HostConfig.NanoCpus}}' <name>`. Memory in bytes, CPUs in nanoCPUs (1 CPU = 1e9). Or `docker stats` shows usage vs limit live. **Q:** (Senior) How do you debug a container that is restart-looping too fast to attach to? **A:** A few options. (1) Check `docker logs --tail 200` between restarts — even short bursts of output land. (2) Override the entrypoint with a sleep so it stays up: `docker run --entrypoint sleep myimg 3600`, then `docker exec` in. (3) Add `--restart=no` so it crashes once and stays exited; then `docker logs` and `docker inspect` give you a stable snapshot. (4) Run the broken image with the actual entrypoint command shifted into a debug shell wrapper. ## Examples ### Quick health-check loop ```bash $ for c in web api db; do s=$(docker inspect --format '{{.State.Status}}' "$c" 2>/dev/null || echo missing) h=$(docker inspect --format '{{.State.Health.Status}}' "$c" 2>/dev/null || echo "-") echo "$c: $s ($h)" done web: running (healthy) api: running (healthy) db: running (starting) ``` State plus health status for several containers at once. Great for shell scripts. ### Tail logs from multiple services in Compose ```bash $ docker compose logs -f --tail 50 # Streams from all services, color-coded by service name. $ docker compose logs -f --tail 50 api db # Only api and db. ``` Compose layers nice multi-service tailing on top of plain `docker logs`. ### Check why the last exited container died ```bash $ docker inspect --format '{{.State.Status}} ({{.State.ExitCode}}) OOM={{.State.OOMKilled}} Err={{.State.Error}}' \ $(docker ps -alq) exited (137) OOM=true Err= # → exited with 137 (SIGKILL); OOMKilled=true → memory limit hit. ``` Four fields together usually pinpoint the cause without opening a JSON dump.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.