15.2.2 Docker Stats
A focused guide to Docker Stats, connecting core concepts with practical Docker and container operations.
The docker stats command is Docker's built-in, real-time resource monitoring interface, streaming a live, continuously updating view of CPU, memory, network, and block I/O usage for one or more running containers directly from the daemon, without requiring any additional monitoring infrastructure to be installed, making it the most immediate tool available for interactive resource inspection.
Basic usage
Run without arguments, docker stats streams live statistics for every running container on the host, updating continuously until interrupted:
docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
3f29a8c1d8e2 my-api 12.4% 210MiB / 512MiB 41.0% 1.2MB / 850kB 45MB/1.2GB 24
a8b9c2d3e4f5 my-db 3.1% 180MiB / 1GiB 17.6% 500kB / 1.1MB 12MB/340MB 8
Specifying one or more container names or IDs restricts the output to just those containers, which is more practical once a host is running enough containers that the full, unfiltered list becomes difficult to scan:
docker stats my-api my-db
Disabling the live stream for scripting
The --no-stream flag captures a single snapshot and exits immediately rather than continuously updating, which is the appropriate mode for scripting or any automated check rather than interactive observation:
docker stats --no-stream my-api
docker stats --no-stream --format "{{.Name}}: {{.CPUPerc}} {{.MemUsage}}" my-api
Without --no-stream, a script attempting to capture output from docker stats would hang indefinitely waiting for the command to exit on its own, since the live mode is designed for continuous terminal display rather than a single, completed invocation.
Custom output formatting
The --format flag accepts a Go template string, allowing output to be restructured into exactly the fields and layout needed, which is particularly useful when piping output into another tool or extracting a specific value for an automated check:
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemPerc}}"
docker stats --no-stream --format '{{json .}}' my-api | jq '.MemPerc'
Requesting JSON output specifically is useful when the result needs to be parsed reliably by a script, since the default table format is meant for human readability and is less convenient to parse programmatically than a structured JSON object.
Filtering by resource type in scripts
Because docker stats reports several different metrics simultaneously, a script checking a specific threshold typically needs to extract just the relevant field rather than parsing the full output:
MEM_PERCENT=$(docker stats --no-stream --format "{{.MemPerc}}" my-api | tr -d '%')
if (( $(echo "$MEM_PERCENT > 90" | bc -l) )); then
echo "Warning: my-api memory usage above 90%"
fi
This kind of lightweight, ad hoc threshold check built directly on docker stats output is reasonable for quick scripts or manual investigation, but is not a substitute for a proper metrics and alerting pipeline for anything beyond occasional, interactive use.
What docker stats does not provide
docker stats has no history; once the command exits or the terminal session ends, the data it displayed is gone, with no way to retrieve what usage looked like five minutes or five hours ago. It also only reports against the local daemon, providing no aggregated, cross-host view for a multi-host deployment, and it does not support alerting, since it is purely a display tool with no concept of thresholds or notifications built in.
docker stats --no-stream
These limitations are precisely what dedicated metrics pipelines (cAdvisor feeding into Prometheus, for instance) are built to address, and docker stats should be understood as a complement to that kind of pipeline for quick, interactive checks, not a replacement for it in any production monitoring context.
Streaming stats for a specific subset during an incident
During an active investigation, narrowing the live stream to just the containers actually suspected of being involved keeps the output focused and easier to read than the full, unfiltered list:
docker stats $(docker ps --filter "label=app=my-app" --format "{{.Names}}")
Combining docker stats with a docker ps filter this way is a quick, effective way to focus on a relevant group of containers, such as every replica of a specific service, without needing to type out each container name individually.
Comparing stats across all containers at once
A common diagnostic pattern during an incident is comparing every container's current resource usage simultaneously to identify whether one specific container stands out, rather than checking each one individually in sequence:
docker stats --no-stream
A single, unfiltered snapshot across every running container, sorted visually by eye for the highest CPU or memory figures, is often the fastest way to identify an outlier container during a host-wide investigation, before narrowing focus to that specific container for deeper inspection.
Common mistakes
- Using
docker statswithout--no-streaminside a script, causing the script to hang waiting for a command that does not exit on its own. - Relying on
docker statsas the primary production monitoring tool, when it provides no history, no cross-host aggregation, and no alerting capability. - Parsing the default human-readable table output in scripts instead of requesting JSON format, leading to fragile parsing logic that breaks if the table layout changes.
- Forgetting that the underlying byte counters for network and block I/O are cumulative totals, not rates, when building a script that checks for a throughput threshold.
- Not filtering to a relevant subset of containers when investigating a specific issue, making it harder to visually identify the actually relevant container among many others in the unfiltered output.
docker stats is the right tool for immediate, interactive resource inspection and quick scripted checks, valuable specifically because it requires no additional infrastructure, but its lack of history, cross-host aggregation, and alerting means production observability still depends on exporting the same underlying cgroups data to a persistent metrics pipeline rather than relying on this command alone.