docker stats
docker stats shows live resource usage for running containers. It matters because many container problems are not crashes: they are slow memory leaks, CPU saturation, unexpected network traffic, disk-heavy writes, or too many processes inside one container.
The command is a quick inspection tool, not a full monitoring platform. Use it when you need an immediate answer at the terminal: which container is busy, how close it is to its memory limit, and whether a service is doing the kind of work you expect.
Overview: How docker stats works
docker stats asks the Docker daemon for resource accounting data about containers. The Docker CLI is only the client; the daemon owns the containers, talks to the container runtime, and reads operating-system counters. On Linux, those counters come mostly from control groups, usually called cgroups. Docker uses cgroups to limit and measure CPU, memory, I/O, and process counts for each container. With Docker Desktop on macOS or Windows, the containers run inside a lightweight Linux VM, so the numbers describe resource use inside that VM, not direct native host processes.
A container is an isolated process tree created from an image. The image is read-only layers, while the running container has a thin writable layer and resource controls around its processes. docker stats does not inspect the image layers, container logs, or application health directly. Instead, it samples runtime counters for the container’s current process tree and streams a table that refreshes until you stop it.
The most useful columns are CPU %, MEM USAGE / LIMIT, MEM %, NET I/O, BLOCK I/O, and PIDS. CPU can be surprising: on a machine with multiple cores, a container using one full core may show about 100%, while a container using two full cores may show about 200%. Memory usage is shown against the container’s configured memory limit. If no explicit limit is set, Docker often shows the host or Docker Desktop VM memory limit, which can make a container look less constrained than it would be in production.
NET I/O is cumulative bytes received and sent through the container’s network interfaces since it started. BLOCK I/O is cumulative disk-like read and write activity through the container’s block devices and writable layer. A write-heavy container might be creating logs, temporary files, database pages, or cache files. PIDS counts processes and threads in the container’s process namespace. A high or steadily rising PID count often points to a worker leak or a process supervisor spawning children that never exit.
Syntax
docker stats [OPTIONS] [CONTAINER...]
Without container names or IDs, Docker shows all running containers. With one or more container names or IDs, it filters the stream to only those containers.
| Option | Purpose |
|---|---|
--all, -a |
Show all containers, including stopped containers. Stopped containers usually display zeroes. |
--no-stream |
Print one snapshot and exit. This is useful in scripts and before-and-after checks. |
--no-trunc |
Do not truncate container IDs in the output. |
--format |
Format the output using Docker’s Go template syntax, such as table {{.Name}} {{.CPUPerc}}. |
The most common forms are:
docker stats
docker stats --no-stream web redis
docker stats --no-stream --format 'table {{.Name}} {{.CPUPerc}} {{.MemUsage}} {{.PIDs}}'
Examples
Watch all running containers
docker run -d --name stats-nginx -p 8080:80 nginx:1.27-alpine
docker stats stats-nginx
Output:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
9b12c8a3152d stats-nginx 0.03% 8.31MiB / 7.65GiB 0.11% 1.2kB / 0B 0B / 0B 5
This starts a small Nginx container and then streams live stats for it. The published port is only there so you can generate traffic if you want to test NET I/O; publishing a port is not required for docker stats. Press Ctrl+C to stop watching. The container keeps running because you stopped only the stats command, not the container.
Take a script-friendly snapshot
docker stats --no-stream stats-nginx
Output:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
9b12c8a3152d stats-nginx 0.01% 8.45MiB / 7.65GiB 0.11% 2.4kB / 0B 0B / 0B 5
--no-stream returns a single measurement and exits. That makes it better for documentation, incident notes, shell scripts, or comparing two points in time. Remember that CPU is a sample, so one snapshot can miss short spikes. For spike hunting, leave the stream open or use a monitoring system that records samples over time.
Format only the fields you need
docker stats --no-stream --format 'table {{.Name}} {{.CPUPerc}} {{.MemPerc}} {{.NetIO}} {{.PIDs}}' stats-nginx
Output:
NAME CPU % MEM % NET I/O PIDS
stats-nginx 0.02% 0.11% 2.4kB / 0B 5
The formatted view removes columns you do not care about. This is helpful when a terminal is narrow or when you want to paste focused evidence into an issue. Docker’s template fields include values such as .Container, .Name, .ID, .CPUPerc, .MemUsage, .MemPerc, .NetIO, .BlockIO, and .PIDs.
Compare containers with memory limits
docker run -d --name limited-nginx --memory 128m nginx:1.27-alpine
docker stats --no-stream limited-nginx
Output:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
2a41a75b93ef limited-nginx 0.04% 8.2MiB / 128MiB 6.41% 656B / 0B 0B / 0B 5
The same small service now shows memory usage against a 128MiB limit instead of the host or VM memory ceiling. That percentage is often the number you need during debugging: a container using 80MiB may be fine with a 1GiB limit but risky with a 96MiB limit.
How it works step by step
- You run
docker statsin a terminal. The Docker CLI connects to the Docker daemon through the configured Docker socket or remote API endpoint. - The daemon resolves the requested container names or IDs. If you did not provide any, it selects all running containers, unless
--allis also present. - For each container, Docker reads resource counters maintained by the host kernel. On Linux this is mainly cgroup data for CPU time, memory usage, I/O, and PID counts.
- Docker calculates rates or percentages from raw counters. CPU percentage is based on how much CPU time the container used between samples compared with available CPU time.
- The daemon sends stats data back to the CLI. In streaming mode, the CLI redraws the table repeatedly. With
--no-stream, it prints one table and exits.
Because these numbers are sampled and calculated, treat them as operational signals rather than perfect accounting. They are excellent for finding direction: which container deserves the next inspection with docker logs, docker exec, application metrics, or profiling tools.
Common Mistakes
Assuming low CPU means the app is healthy
docker stats --no-stream api
Output:
NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
api 0.00% 312MiB / 512MiB 60.94% 0B / 0B 0B / 0B 37
This container is not using CPU, but that does not prove it is healthy. It may be deadlocked, waiting on a database, unable to accept network traffic, or stuck before its server starts. Fix the investigation by combining docker stats with docker logs api, docker inspect api, and an application-level health check.
Reading memory percentage without checking the limit
docker stats --no-stream worker
Output:
NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
worker 4.11% 900MiB / 15.6GiB 5.63% 18MB / 2MB 4MB / 128MB 22
A low memory percentage can hide a problem if the container has no production-like memory limit. The same 900MiB worker could be near failure in production with a 1GiB limit. Fix this by testing with realistic limits, for example docker run --memory 1g, and by alerting on absolute memory usage as well as percentage.
Expecting docker stats to show stopped containers by default
docker stats old-job
Output:
Error response from daemon: No such container: old-job
If the name is wrong, removed, or not visible to the current Docker context, Docker cannot show it. If the container exists but is stopped, plain docker stats is not the best tool for historical diagnosis. Use docker ps -a to confirm its state, docker logs old-job for prior output, and docker inspect old-job for exit status and configuration. Use docker stats --all --no-stream only when you specifically want stopped containers included in the table.
Best Practices
- Use
docker statsearly in an incident to identify the busiest or most constrained container, then switch to logs, traces, profiles, or application metrics for root cause. - Use
--no-streamwhen saving evidence in a ticket or comparing before and after a change. - Set realistic memory limits in test and staging so
MEM %resembles production behavior. - Watch absolute memory values, not only percentages, especially on Docker Desktop where the VM limit may differ from the physical machine.
- Investigate rising
PIDScounts. Containers should usually have a predictable process and thread range. - Treat high
BLOCK I/Oas a clue. It may indicate excessive logging, cache churn, temporary files, or a database workload that needs a volume and proper tuning. - Clean up containers created during practice with
docker rm -f stats-nginx limited-nginxwhen you are done.
Practice Exercises
- Start an
nginx:1.27-alpinecontainer with a name of your choice, rundocker statsagainst only that container, then stop the stats stream without stopping the container. Hint: use the container name as the final argument. - Run the same container with a
64mmemory limit and take a one-line snapshot. Expected end state: the memory limit column should show roughly64MiB, not the full host or VM memory limit. - Create a formatted stats command that prints only the name, memory usage, block I/O, and PID count. Hint: use
--formatwith atabletemplate.
Summary
docker statsstreams live container resource usage from Docker daemon and kernel accounting data.- The key columns are CPU percentage, memory usage and limit, network I/O, block I/O, and PID count.
--no-streamgives one snapshot;--formatmakes focused terminal or script output.- Percentages only make sense when you understand the limit they are based on.
- Use
docker statsas a fast debugging signal, then confirm the cause with logs, inspect data, and application metrics.
