✦ For everyone, free.

Practical knowledge for real and everyday life

Home

15.2.2.4 Stats CPU Percent

A focused guide to Stats CPU Percent, connecting core concepts with practical Docker and container operations.

Stats CPU percent is the figure docker stats displays summarizing a container's processor consumption, and its specific normalization, relative to a single core rather than relative to the host's total core count by default, is the single most common source of confusion when comparing this number against other tools or against intuition about what "100% CPU" should mean.

The default normalization

By default, the percentage Docker reports treats 100% as the equivalent of one full CPU core's worth of usage, which means a container using two full cores simultaneously reports 200%, not 100%:

docker stats --no-stream --format "{{.CPUPerc}}" my-api
187.3%

This differs from how some other tools, including certain configurations of top, normalize against the total number of cores available, where 100% would represent full utilization of every core combined; the two conventions are both legitimate but answer slightly different questions, and conflating them when comparing numbers from different tools is a common source of confusion.

Comparing against a configured CPU limit

When a container has an explicit --cpus limit set, that limit becomes the practically meaningful ceiling for the percentage, and the displayed figure should be read relative to that ceiling rather than relative to the host's total core count:

docker run -d --cpus=2 my-api
docker stats --no-stream --format "{{.CPUPerc}}" my-api
196.0%

A reading of 196% against a 2-CPU limit means the container is using nearly its full allotment; the same raw number for a container with no limit set at all on a host with 16 cores available would represent a much smaller fraction of total host capacity, which is why the limit, or lack of one, fundamentally changes what a given percentage figure actually means.

Why percentages above 100% confuse newcomers

Operators coming from a background of monitoring single-threaded processes, where 100% genuinely represents full utilization of the one resource being measured, often initially assume a reading like 250% must indicate an error or an impossible value, when it is simply correctly reflecting multi-core, multi-threaded usage summed across more than one core:

docker exec my-api nproc

Checking how many CPUs are actually visible to and usable by the container clarifies the practical ceiling: a container with access to 4 cores can legitimately report up to 400% under full, sustained multi-core load.

CPU percent and throttling are different signals

A container's CPU percent reading staying below its configured limit does not necessarily mean it has never been throttled; throttling and the percentage figure are measuring different things, instantaneous usage during the sampling window versus whether the kernel has needed to restrict the container's CPU access during any portion of a given accounting period:

cat /sys/fs/cgroup/cpu/docker/<container-id>/cpu.stat
nr_throttled 87

A container that is frequently hitting and being throttled at its limit can still show an average CPU percent reading that appears comfortably below 100% of that limit when sampled at a coarse interval, since brief throttling events between samples are not necessarily reflected in a periodic snapshot the way a direct throttling count is.

Sustained versus burst CPU percent

A brief spike to a high CPU percentage during a short burst of work is a fundamentally different situation than the same reading sustained continuously over an extended period, and treating both identically when deciding whether a container needs a higher CPU allocation can lead to over-provisioning for what was actually just a normal, transient burst:

avg_over_time(container_cpu_usage_seconds_total{name="my-api"}[10m])

Reviewing an averaged trend over a representative window, rather than reacting to a single instantaneous reading, distinguishes a genuinely CPU-constrained service from one that simply experiences occasional, normal bursts that briefly approach its configured limit without representing a persistent capacity problem.

Aggregate percent across multiple containers

When comparing CPU percent across several containers on the same host, especially containers with different configured limits, the raw percentages are not directly comparable without also accounting for each container's respective limit, since one container's 80% (against a 1-CPU limit) represents far less absolute CPU consumption than another's 80% (against a 4-CPU limit):

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

Converting each container's percentage into an absolute, comparable unit, actual CPU-seconds consumed, rather than comparing the raw percentages directly, gives a more accurate picture when ranking containers by genuine CPU consumption across a host with mixed limit configurations.

Common mistakes

  • Assuming a CPU percentage above 100% indicates an error rather than correctly reflecting multi-core usage.
  • Comparing docker stats CPU percentages directly against figures from a tool that normalizes against total host cores instead of a single core, without accounting for the difference in convention.
  • Treating a CPU percentage that appears below the configured limit as proof the container has never been throttled, without separately checking the actual throttling counters.
  • Reacting to a single high instantaneous CPU percent reading as evidence of a persistent capacity problem, without checking whether it reflects a brief, normal burst instead.
  • Comparing raw CPU percentages across containers with different configured CPU limits as if they were directly comparable without normalizing for each container's actual allotment.

Stats CPU percent is calculated correctly and consistently by Docker, but its single-core-relative normalization, its relationship (or lack of a direct one) to actual throttling events, and the need to account for each container's specific configured limit are all details that change what a given reading actually means, and overlooking any of them is the most common source of confusion when working with this otherwise straightforward metric.