| |

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:

FieldContents
IdFull container ID
CreatedCreation timestamp
PathEntry point command
ArgsCommand arguments
StateStatus, running, exit code, timestamps
ImageImage ID
ConfigEnvironment, command, labels, exposed ports
HostConfigPort bindings, mounts, restart policy, resources
NetworkSettingsIP address, ports, networks
MountsVolume 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:

ColumnMeaning
CONTAINER IDShort container ID
NAMEContainer name
CPU %CPU usage as a percentage
MEM USAGE / LIMITMemory used versus the limit
MEM %Memory usage as a percentage of the limit
NET I/ONetwork bytes received and sent
BLOCK I/OBlock device bytes read and written
PIDSNumber 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

FlagPurpose
(none)Full JSON output
-f / --formatGo template to extract a field
-s / --sizeInclude file sizes
--typeFilter by object type

Common Inspect Paths

PathValue
.State.Statusrunning, exited, paused
.State.PidHost PID of main process
.State.ExitCodeExit code
.State.OOMKilledWhether OOM-killed
.State.StartedAtStart timestamp
.Config.EnvEnvironment variables
.Config.ImageImage name
.HostConfig.MemoryMemory limit
.HostConfig.RestartPolicy.NameRestart policy
.NetworkSettings.IPAddressContainer IP
.NetworkSettings.PortsPort mappings
.MountsVolume and bind mounts

docker stats Flags

FlagPurpose
(none)Live updating
--no-streamSingle snapshot
-aInclude stopped containers
--formatTemplate output

docker stats Columns

ColumnMeaning
CPU %Percent of one CPU core
MEM USAGE / LIMITUsed versus limit
MEM %Percent of limit
NET I/ONetwork bytes in/out
BLOCK I/ODisk bytes read/written
PIDSProcess and thread count

docker top Usage

CommandEffect
docker top webDefault ps output
docker top web auxCustom ps options
docker top web -efFull 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

PitfallWhy It HappensFix
Wrong inspect pathField is nestedUse jq or check the JSON structure
Template errorBad Go template syntaxCheck field names and braces
Stats never exitsLive mode by defaultUse --no-stream for scripting
Memory limit shows host totalNo --memory setInterpret as “no limit”
docker top PIDs differ from containerHost PIDs vs container PIDsCompare with docker exec ps
Name has leading slashDocker names are pathsStrip 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

ItemValue
docker inspectFull JSON metadata for a container
--formatExtract a field with Go templates
.State.Statusrunning, exited, paused
.State.ExitCodeContainer exit code
.State.OOMKilledWhether OOM-killed
.Config.EnvEnvironment variables
.HostConfig.MemoryMemory limit
.MountsVolume and bind mounts
docker statsLive CPU, memory, network, I/O usage
--no-streamSingle snapshot for scripting
docker topProcesses inside a container
jqExternal JSON processor for complex queries

Key takeaways:

  • docker inspect is 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 --format or jq to extract the field you need.
  • docker stats reports live resource usage. CPU, memory, network, and I/O are shown in a table. The --no-stream flag returns a single snapshot for scripting, and --format customizes the output.
  • docker top shows the processes inside a container from the host’s perspective. The PIDs are host PIDs, not container PIDs. Custom ps options 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.OOMKilled field is true when the kernel killed the container for exceeding its memory limit.
  • jq is more expressive than Go templates for complex queries. For nested structures and filters, piping docker inspect to jq is 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!