Docker 9 🐳 Inspecting Container Metadata: docker inspect, docker stats, and docker top
Running a container is only half the job. Knowing what it is actually doing—what configuration it was created with, how much CPU and memory it is consuming, what processes it is running—requires a set of inspection commands that read metadata rather than run new operations. docker inspect returns the full JSON description of a container, docker stats reports live resource usage, and docker top shows the processes running inside a container from the host’s perspective. Together they answer the questions that arise when a container behaves unexpectedly or when you need to verify that it was configured correctly.
These commands are read-only. They do not modify the container, do not start or stop processes, and do not require the container to be healthy. They work on running and stopped containers alike, which makes them the first tool to reach for when diagnosing a problem. The difference between them is scope: inspect gives the static configuration and state, stats gives the dynamic resource usage, and top gives the process list.
The challenge with docker inspect is not running it but reading its output. The JSON is comprehensive—hundreds of fields covering configuration, networking, mounts, state, and history—and extracting the relevant value requires either --format with Go templates or an external JSON processor. Understanding the structure and the common paths pays off immediately because most Docker problems are diagnosed by reading a specific field.
This chapter covers docker inspect with its JSON structure and format templates, docker stats with its live and one-shot modes, and docker top for process inspection, along with the patterns for extracting the information these commands provide.
Key point: docker inspect returns the full configuration and state of a container as JSON. docker stats reports live CPU, memory, network, and I/O usage. docker top lists the processes running inside a container. All three are read-only and work on running and stopped containers.
Why inspection commands matter
The configuration-verification problem. A container may not behave as expected because it was created with the wrong environment variable, the wrong port mapping, the wrong volume, or the wrong restart policy. docker inspect returns exactly what was configured, which is the ground truth for diagnosing configuration problems.
The resource-diagnosis problem. A container that is slow, unresponsive, or being killed by the OOM killer is exhibiting a resource problem. docker stats shows CPU and memory usage in real time, so you can see whether the container is hitting a limit, whether it is starved, and whether the problem is CPU or memory.
The process-visibility problem. A container that runs multiple processes, or that spawns children, may have a process tree that is not obvious from the Dockerfile. docker top shows the processes from the host’s perspective, including their PIDs on the host and their PIDs inside the container.
The scripting problem. Inspection commands are used in automation. A deployment script may check docker inspect to verify that a container is healthy before proceeding, or a monitoring script may poll docker stats to collect metrics. The --format flag makes the output machine-readable.
The post-mortem problem. When a container has stopped, docker inspect still works. The exit code, the error message, the start and finish times, and the configuration are all preserved. This makes inspect the primary tool for understanding why a container died.
a. docker inspect: full container metadata
docker inspect returns a JSON array containing the full description of one or more containers.
docker inspect web
The output is a large JSON document. The top-level fields include:
| Field | Contents |
|---|---|
Id | Full container ID |
Created | Creation timestamp |
Path | Entry point command |
Args | Command arguments |
State | Status, running, exit code, timestamps |
Image | Image ID |
Config | Environment, command, labels, exposed ports |
HostConfig | Port bindings, mounts, restart policy, resources |
NetworkSettings | IP address, ports, networks |
Mounts | Volume and bind mount details |
The State field is the most commonly inspected:
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 12345,
"ExitCode": 0,
"Error": "",
"StartedAt": "2026-01-01T10:00:00Z",
"FinishedAt": "0001-01-01T00:00:00Z"
}
The Config.Env field lists environment variables, HostConfig.PortBindings lists port mappings, Mounts lists volumes, and NetworkSettings.Networks lists the container’s network attachments.
Format templates (-f or --format) extract a single value using Go template syntax:
docker inspect --format '{{.State.Status}}' web
# running
docker inspect --format '{{.State.Pid}}' web
# 12345
docker inspect --format '{{.NetworkSettings.IPAddress}}' web
# 172.17.0.2
docker inspect --format '{{json .Config.Env}}' web
# ["PATH=/usr/local/bin", ...]
The json function serializes a field back to JSON, which is useful for arrays and objects.
Multiple containers can be inspected at once, and the template applies to each:
docker inspect --format '{{.Name}}: {{.State.Status}}' web db cache
# /web: running
# /db: running
# /cache: exited
The container name in the output has a leading slash. Use --format '{{.Name}}' and strip it, or use {{.Name}} with strings.TrimPrefix if the template supports it.
Extracting the exit code of a stopped container:
docker inspect --format '{{.State.ExitCode}}' web
Checking whether a container was OOM-killed:
docker inspect --format '{{.State.OOMKilled}}' web
Finding the restart policy:
docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' web
Listing all environment variables:
docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' web
The range action iterates over a list, and println adds a newline after each element.
Listing all mounts:
docker inspect --format '{{range .Mounts}}{{.Source}} → {{.Destination}}{{println}}{{end}}' web
For complex queries, pipe the JSON to jq:
docker inspect web | jq '.[0].NetworkSettings.Networks'
docker inspect web | jq '.[0].State.ExitCode'
docker inspect web | jq '.[0].HostConfig.PortBindings'
jq is more expressive than Go templates for filtering and transforming JSON, and it handles nested structures more clearly.
b. docker stats: live resource usage
docker stats reports live CPU, memory, network, and block I/O usage for running containers.
docker stats
The default output is a live-updating table:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
abc123 web 0.50% 45.2MiB / 512MiB 8.83% 1.2kB / 0B 0B / 0B 5
def456 db 2.30% 320MiB / 1GiB 31.25% 5.4MB / 3.2MB 12MB / 8MB 12
The columns are:
| Column | Meaning |
|---|---|
CONTAINER ID | Short container ID |
NAME | Container name |
CPU % | CPU usage as a percentage |
MEM USAGE / LIMIT | Memory used versus the limit |
MEM % | Memory usage as a percentage of the limit |
NET I/O | Network bytes received and sent |
BLOCK I/O | Block device bytes read and written |
PIDS | Number of processes and threads |
Specific containers can be named:
docker stats web db
No-stream mode (--no-stream) returns a single snapshot and exits, which is useful for scripting:
docker stats --no-stream web
Format templates work here too:
docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}'
Common placeholders include .Name, .CPUPerc, .MemUsage, .MemPerc, .NetIO, .BlockIO, and .PIDs.
All containers (-a) includes stopped ones, though they show zero usage:
docker stats -a
The CPU percentage in docker stats is the percentage of one CPU core. A container using two full cores shows 200%. The memory limit shown is the container’s limit, which is the host’s total memory if no --memory flag was set. This means a container without a memory limit shows MEM USAGE / LIMIT as used versus the total host memory, which is not a limit at all.
c. docker top: processes inside a container
docker top lists the processes running inside a container, from the host’s perspective. It uses the ps command on the host with the container’s PID namespace.
docker top web
The output resembles ps output:
UID PID PPID C STIME TTY TIME CMD
root 12345 12300 0 10:00 ? 00:00:00 nginx: master process nginx
www-data 12346 12345 0 10:00 ? 00:00:00 nginx: worker process
The PIDs are the host PIDs, not the container PIDs. The container sees its init as PID 1, but the host sees it as a different number. To map between them, use docker inspect --format '{{.State.Pid}}' for the container’s main process, or docker exec to run ps inside the container.
docker top accepts custom ps options as arguments:
docker top web aux
docker top web -ef
The options are passed to the host’s ps command, so any option the host supports works.
docker top is useful for:
- Verifying that the expected processes are running.
- Checking the process tree and parent-child relationships.
- Confirming that a process is running as the expected user.
- Counting the number of processes for diagnosing fork bombs.
For a container with a single process, docker top shows one row. For a container that spawns workers or children, it shows the full tree.
Complete Example Session
# ============================================
# PART 1: RUN A CONTAINER
# ============================================
docker run -d --name web -p 8080:80 --memory=256m nginx
# ============================================
# PART 2: FULL INSPECT OUTPUT
# ============================================
docker inspect web
# ============================================
# PART 3: EXTRACT STATE
# ============================================
docker inspect --format '{{.State.Status}}' web
docker inspect --format '{{.State.Pid}}' web
docker inspect --format '{{.State.ExitCode}}' web
# ============================================
# PART 4: EXTRACT NETWORK INFO
# ============================================
docker inspect --format '{{.NetworkSettings.IPAddress}}' web
docker inspect --format '{{json .NetworkSettings.Ports}}' web
# ============================================
# PART 5: EXTRACT CONFIGURATION
# ============================================
docker inspect --format '{{.HostConfig.Memory}}' web
docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' web
# ============================================
# PART 6: LIST ENVIRONMENT VARIABLES
# ============================================
docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' web
# ============================================
# PART 7: LIST MOUNTS
# ============================================
docker inspect --format '{{range .Mounts}}{{.Source}} → {{.Destination}}{{println}}{{end}}' web
# ============================================
# PART 8: LIVE STATS
# ============================================
docker stats web
# Press Ctrl+C to exit
# ============================================
# PART 9: ONE-SHOT STATS WITH FORMAT
# ============================================
docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}'
# ============================================
# PART 10: PROCESSES INSIDE THE CONTAINER
# ============================================
docker top web
docker top web aux
These ten parts cover running a container, full inspection, extracting state, network, and configuration, listing environment variables and mounts, live and one-shot stats, and process listing.
Quick Reference
docker inspect Flags
| Flag | Purpose |
|---|---|
| (none) | Full JSON output |
-f / --format | Go template to extract a field |
-s / --size | Include file sizes |
--type | Filter by object type |
Common Inspect Paths
| Path | Value |
|---|---|
.State.Status | running, exited, paused |
.State.Pid | Host PID of main process |
.State.ExitCode | Exit code |
.State.OOMKilled | Whether OOM-killed |
.State.StartedAt | Start timestamp |
.Config.Env | Environment variables |
.Config.Image | Image name |
.HostConfig.Memory | Memory limit |
.HostConfig.RestartPolicy.Name | Restart policy |
.NetworkSettings.IPAddress | Container IP |
.NetworkSettings.Ports | Port mappings |
.Mounts | Volume and bind mounts |
docker stats Flags
| Flag | Purpose |
|---|---|
| (none) | Live updating |
--no-stream | Single snapshot |
-a | Include stopped containers |
--format | Template output |
docker stats Columns
| Column | Meaning |
|---|---|
CPU % | Percent of one CPU core |
MEM USAGE / LIMIT | Used versus limit |
MEM % | Percent of limit |
NET I/O | Network bytes in/out |
BLOCK I/O | Disk bytes read/written |
PIDS | Process and thread count |
docker top Usage
| Command | Effect |
|---|---|
docker top web | Default ps output |
docker top web aux | Custom ps options |
docker top web -ef | Full format |
Best Practices
✅ Do This:
docker inspect --format '{{.State.Status}}' web # Scriptable check
docker inspect web | jq '.[0].NetworkSettings.Networks' # Complex query
docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}' # Snapshot
docker top web # Verify processes
docker inspect --format '{{.State.OOMKilled}}' web # Diagnose OOM
❌ Don’t Do This:
docker inspect web # ❌ Reading full JSON manually
docker stats # ❌ In scripts without --no-stream
docker top web | grep nginx # ❌ Works but loses context
docker inspect --format '{{.IPAddress}}' web # ❌ Wrong path
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Wrong inspect path | Field is nested | Use jq or check the JSON structure |
| Template error | Bad Go template syntax | Check field names and braces |
| Stats never exits | Live mode by default | Use --no-stream for scripting |
| Memory limit shows host total | No --memory set | Interpret as “no limit” |
| docker top PIDs differ from container | Host PIDs vs container PIDs | Compare with docker exec ps |
| Name has leading slash | Docker names are paths | Strip with sed or jq |
Real-World Examples
1. Check Container Status
docker inspect --format '{{.State.Status}}' web
2. Get Container IP
docker inspect --format '{{.NetworkSettings.IPAddress}}' web
3. Check Exit Code
docker inspect --format '{{.State.ExitCode}}' web
4. Detect OOM Kill
docker inspect --format '{{.State.OOMKilled}}' web
5. List Environment
docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' web
6. Check Restart Policy
docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' web
7. Live Resource Usage
docker stats
8. Snapshot for Monitoring
docker stats --no-stream --format '{{.Name}},{{.CPUPerc}},{{.MemPerc}}'
9. Process List
docker top web aux
10. Complex Query with jq
docker inspect web | jq '.[0].Mounts[] | {source, destination}'
Visual
docker inspect Structure
┌──────────────────────────────────────────────────────────────┐
│ docker inspect web │
│ │ │
│ ▼ │
│ [ │
│ { │
│ "Id": "abc123...", │
│ "Created": "2026-01-01T10:00:00Z", │
│ "State": { │
│ "Status": "running", │
│ "Pid": 12345, │
│ "ExitCode": 0, │
│ "OOMKilled": false │
│ }, │
│ "Config": { │
│ "Image": "nginx", │
│ "Env": ["PATH=...", "NGINX_VERSION=..."] │
│ }, │
│ "HostConfig": { │
│ "Memory": 268435456, │
│ "RestartPolicy": {"Name": "unless-stopped"} │
│ }, │
│ "NetworkSettings": { │
│ "IPAddress": "172.17.0.2", │
│ "Ports": {"80/tcp": [{"HostPort": "8080"}]} │
│ }, │
│ "Mounts": [ │
│ {"Source": "/data", "Destination": "/app"} │
│ ] │
│ } │
│ ] │
└──────────────────────────────────────────────────────────────┘
Extracting Fields with –format
┌──────────────────────────────────────────────────────────────┐
│ docker inspect --format '{{.State.Status}}' web │
│ │ │
│ └── running │
│ │
│ docker inspect --format '{{.State.Pid}}' web │
│ │ │
│ └── 12345 │
│ │
│ docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' web
│ │ │
│ ├── PATH=/usr/local/bin:... │
│ ├── NGINX_VERSION=1.25.0 │
│ └── ... │
│ │
│ Use {{json .Field}} for arrays and objects. │
└──────────────────────────────────────────────────────────────┘
docker stats Output
┌──────────────────────────────────────────────────────────────┐
│ CONTAINER NAME CPU % MEM USAGE/LIMIT MEM % PIDS │
│ ───────────────────────────────────────────────────────── │
│ abc123 web 0.50% 45.2MiB/256MiB 17.7% 5 │
│ def456 db 2.30% 320MiB/1GiB 31.2% 12 │
│ │
│ CPU % is percent of ONE core. 200% = two full cores. │
│ MEM LIMIT shows host total if --memory was not set. │
│ --no-stream returns one snapshot and exits. │
└──────────────────────────────────────────────────────────────┘
docker top vs docker exec ps
┌──────────────────────────────────────────────────────────────┐
│ docker top web docker exec web ps aux │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ Host PIDs │ │ Container PIDs │ │
│ │ PID 12345 nginx │ │ PID 1 nginx │ │
│ │ PID 12346 worker │ │ PID 7 worker │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
│ Same processes, different PID namespaces. │
│ docker top runs on the host; exec runs inside. │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
docker inspect | Full JSON metadata for a container |
--format | Extract a field with Go templates |
.State.Status | running, exited, paused |
.State.ExitCode | Container exit code |
.State.OOMKilled | Whether OOM-killed |
.Config.Env | Environment variables |
.HostConfig.Memory | Memory limit |
.Mounts | Volume and bind mounts |
docker stats | Live CPU, memory, network, I/O usage |
--no-stream | Single snapshot for scripting |
docker top | Processes inside a container |
jq | External JSON processor for complex queries |
Key takeaways:
docker inspectis the ground truth for configuration and state. It returns the full JSON description of a container, including environment, ports, mounts, restart policy, and exit code. Use--formatorjqto extract the field you need.docker statsreports live resource usage. CPU, memory, network, and I/O are shown in a table. The--no-streamflag returns a single snapshot for scripting, and--formatcustomizes the output.docker topshows the processes inside a container from the host’s perspective. The PIDs are host PIDs, not container PIDs. Custompsoptions can be passed as arguments.- All three commands are read-only. They work on running and stopped containers and do not modify anything. This makes them safe for diagnosis.
- CPU percentage is relative to one core. A container using 200% CPU is using two cores. The memory limit shows the host’s total memory when no limit is set.
- Exit codes reveal what happened. Code 0 is a clean exit, 137 is SIGKILL, 143 is SIGTERM.
docker inspect --format '{{.State.ExitCode}}'retrieves it. - OOM kills are visible in inspect. The
.State.OOMKilledfield istruewhen the kernel killed the container for exceeding its memory limit. jqis more expressive than Go templates for complex queries. For nested structures and filters, pipingdocker inspecttojqis often clearer than writing a template.
Remember: Inspection commands answer the question “what is this container doing and how was it configured.” docker inspect gives the static configuration and state—the environment variables, port mappings, mounts, restart policy, and exit code. docker stats gives the dynamic resource usage—the CPU and memory the container is consuming right now. docker top gives the process tree—what is actually running inside the container. These three commands are the first tools to reach for when a container misbehaves, and they are the foundation of automated monitoring and health checks. The output of docker inspect is verbose, so extracting a single field with --format or jq is the practical way to use it. The output of docker stats is live by default, so --no-stream is required for scripting. And docker top complements the other two by showing the processes that produce the resource usage and the logs.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!