July 12, 2026 · Docker HQ Team

The docker stats Command: A Complete Guide

The docker stats command shows a live stream of resource usage for your running containers: CPU percentage, memory usage and limit, network I/O, block I/O, and PID count. Run it with no arguments to watch every running container update roughly once per second, or pass container names to narrow it down. It reads the same cgroup counters the kernel uses to enforce limits, so the numbers are accurate at the instant they refresh.

docker stats

That is the whole command for most situations. The rest of this guide covers what each column actually means, how to reshape the output for scripts, and the places where docker stats will quietly mislead you.

Reading the default output

Run docker stats on a host with a couple of containers and you get something like this:

CONTAINER ID   NAME       CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O         PIDS
a1b2c3d4e5f6   api        3.42%     412.5MiB / 2GiB       20.14%    1.2MB / 3.4MB     0B / 8.19kB       24
f6e5d4c3b2a1   postgres   0.87%     1.1GiB / 2GiB         55.00%    880kB / 1.1MB     45MB / 120MB      12

Here is what each column tells you:

  • CPU % is the container's share of total host CPU. On a 4-core host, a single-threaded process pinned at 100% of one core shows as roughly 25%, not 100%. This trips people up constantly. A value above 100% means the container is using more than one core.
  • MEM USAGE / LIMIT is current memory against the limit. If you never set --memory, the limit shows as the total host RAM, which makes MEM % meaningless for capacity planning.
  • MEM % is usage divided by that limit.
  • NET I/O and BLOCK I/O are cumulative totals since the container started, not rates. They only reset when the container restarts.
  • PIDS is the number of processes or threads inside the container. Watch this if you suspect a fork bomb or a thread leak.

The output refreshes in place and does not scroll. It is a dashboard, not a log.

The flags you will actually use

Snapshot instead of stream

The live stream is useless in a script because it never exits. Add --no-stream to grab one reading and return:

docker stats --no-stream

This is the flag you want in cron jobs, health checks, and anything that pipes output somewhere.

Stats for specific containers

Pass names or IDs to limit the view:

docker stats api postgres

Include stopped containers

By default you only see running containers. Add --all (or -a) to include stopped ones, though their stats will read as zero:

docker stats --all

Custom columns with --format

The default table is wide. If you only care about CPU and memory, --format takes a Go template and trims the rest:

docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

The available placeholders include .Container, .Name, .ID, .CPUPerc, .MemUsage, .MemPerc, .NetIO, .BlockIO, and .PIDs. Drop the table prefix to get raw lines with no header, which is easier to parse:

docker stats --no-stream --format "{{.Name}} {{.CPUPerc}} {{.MemPerc}}"

For structured output, ask for JSON:

docker stats --no-stream --format json

That gives you one JSON object per line, ready to pipe into jq.

Turning stats into something you can act on

A common need is sorting containers by memory so you can spot the hog. docker stats has no sort flag, so pipe it:

docker stats --no-stream --format "{{.Name}}\t{{.MemUsage}}" | sort -k2 -h -r

To find containers over a CPU threshold and alert on them, jq does the filtering:

docker stats --no-stream --format json \
  | jq -r 'select((.CPUPerc | rtrimstr("%") | tonumber) > 80) | .Name'

That prints the name of any container burning more than 80% CPU. Wire it into a cron job and you have a crude but working alerting loop. Crude is the operative word, which brings us to the limits.

Where the docker stats command falls short

docker stats is a point-in-time reading against the current process. It has no memory of the past and no way to reach a container running on another host. A few specific gaps:

No history. Close the terminal and the data is gone. If a container spiked to 100% CPU at 3am and got OOM-killed, docker stats will never tell you. It only shows what is happening right now.

One host at a time. The command talks to the local Docker daemon. Managing five servers means five SSH sessions and five terminals. There is no built-in aggregation.

CPU % is a moving average, not a peak. The value is computed between refresh intervals. Short bursts get smoothed away. A container that hits 100% for 200ms every second may show a modest average while still causing latency spikes.

Memory numbers include cache. The MEM USAGE figure counts page cache the kernel can reclaim under pressure. A container that looks like it is at 90% of its limit may have plenty of reclaimable cache, so it is not actually close to an OOM kill. For the number that matters, check memory.stat inside the cgroup or watch for actual OOM events in dmesg.

For a full walkthrough of the counters behind these figures, see our guide on how to monitor Docker container CPU and memory usage, which digs into what the kernel is actually reporting.

From spot checks to real monitoring

docker stats is the right tool when you are already SSHed into a box and want to know what is eating resources this second. It is the wrong tool for knowing whether a container fell over an hour ago, or for keeping an eye on a fleet.

For anything ongoing you want stored metrics and alerts. The self-hosted route is Prometheus plus cAdvisor plus Grafana, which scrapes the same cgroup data on a schedule and keeps history. That stack is powerful and worth setting up if you run infrastructure full time. It is also a fair amount to maintain. Our post on Docker container monitoring compares the options in more detail.

If you would rather not run a monitoring stack yourself, Docker HQ connects to your hosts over SSH and shows the same live CPU, memory, and network figures on your phone, then sends a push notification when a container or host goes down. It is aimed at exactly the case docker stats cannot cover: knowing something broke without watching a terminal. Small teams tend to reach for this instead of a full Prometheus deployment, and we wrote up why in Docker uptime monitoring for small teams. You can see what it includes on the pricing page.

Start with docker stats --no-stream next time a box feels slow. It will point you at the guilty container in about two seconds. Then decide whether you need the version that remembers.

Frequently asked questions

Why does docker stats show CPU over 100%?

CPU % is measured against a single core, so a container using multiple cores exceeds 100%. A value of 250% means it is using two and a half cores' worth of CPU. This is expected on multi-core hosts and is not an error.

How do I get docker stats to exit instead of streaming?

Add the --no-stream flag: docker stats --no-stream. It takes one reading, prints it, and returns to the prompt. Use this in any script, cron job, or command substitution where a never-ending stream would hang.

Does docker stats slow down my containers?

The overhead is negligible for a quick check. It reads cgroup counters the kernel already maintains. Leaving the live stream running for hours in a terminal is harmless too, though it serves no purpose since it keeps no history. Use --no-stream when you just need a number.

Why does memory usage look higher than my application uses?

The MEM USAGE column includes reclaimable page cache, not just your application's working set. The kernel frees that cache under memory pressure, so a container near its limit is often not close to an OOM kill. To see the working set, inspect memory.stat in the container's cgroup or check dmesg for actual OOM-kill events.