Skip to main content

How to inspect container state, logs, and metadata?

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.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Comments

No comments yet