15.2.2.3 Stats Memory Percent
A focused guide to Stats Memory Percent, connecting core concepts with practical Docker and container operations.
Stats memory percent is the figure docker stats displays as a container's memory usage relative to its configured limit, and while it appears as a simple, single number, correctly interpreting what it actually represents, and recognizing the specific cases where it can be misleading, requires understanding both how the percentage is calculated and what counts toward the usage figure in its numerator.
How the percentage is calculated
The displayed memory percentage is a straightforward division of current usage by the configured limit, multiplied by 100:
docker stats --no-stream --format "{{.MemPerc}}" my-api
41.0%
const memPercent = (memoryStats.usage / memoryStats.limit) * 100;
The simplicity of this calculation is exactly why the inputs matter so much: a misleading numerator (usage) or an unexpected denominator (limit) produces a misleading percentage even though the arithmetic itself is correct.
What counts as usage in the numerator
The usage figure feeding this calculation generally includes page cache in addition to genuine application memory allocation, which means the percentage can appear higher than what the application's own internal memory accounting would suggest, particularly for containers performing significant file I/O:
docker exec my-api cat /sys/fs/cgroup/memory/memory.stat | grep -E '^(rss|cache)'
rss 184320000
cache 98304000
A container reporting 80% memory usage that is mostly reclaimable cache is in a meaningfully different position than one reporting the same 80% through genuine application allocations, even though the displayed percentage alone does not distinguish between them.
What the denominator actually represents
When no explicit --memory limit has been set on a container, the denominator used for the percentage calculation typically falls back to the host's total available memory, which means the percentage in that case reflects usage relative to the entire host's capacity rather than any boundary actually being enforced against the container:
docker run -d my-api
docker stats --no-stream --format "{{.MemUsage}}" my-api
210MiB / 15.6GiB
A low percentage in this scenario does not indicate the container has comfortable headroom against any real constraint; it indicates only that the container is using a small fraction of the host's total memory, with no actual limit in place that would ever trigger an OOM kill regardless of how high usage climbed.
Comparing the percentage against a meaningful threshold
Setting an alerting threshold against this percentage is only meaningful when an explicit --memory limit is actually configured, since the threshold's significance, "this container is approaching the point where it will be OOM-killed", depends entirely on the denominator representing a real, enforced boundary:
docker run -d --memory=512m my-api
sum(container_memory_usage_bytes{name="my-api"}) / sum(container_spec_memory_limit_bytes{name="my-api"}) > 0.85
An alert built on this ratio for a container with no configured limit would never meaningfully fire at a useful threshold, since the denominator (host total memory) is typically far larger than any realistic application memory usage, making the percentage almost always low regardless of actual application memory health.
Percentage trends versus absolute usage trends
For containers with limits that change over time, perhaps adjusted during a capacity review, tracking absolute memory usage in addition to the percentage avoids a misleading read where the percentage appears to improve simply because the limit was raised, even though absolute usage continued growing at the same underlying rate:
container_memory_usage_bytes{name="my-api"}
container_memory_usage_bytes{name="my-api"} / container_spec_memory_limit_bytes{name="my-api"}
Graphing both the absolute figure and the percentage together distinguishes a genuine improvement in memory efficiency from one that is purely an artifact of having raised the limit without addressing the underlying usage growth.
Memory percent during transient spikes
A brief, expected spike in memory percentage, during a scheduled batch job or a cache warming sequence immediately after a deployment, is different from a sustained high percentage that persists under steady-state load, and treating both identically in an alerting configuration can produce either too many false alarms or, if the threshold is tuned to tolerate spikes, a delayed response to a genuine, sustained problem:
avg_over_time(container_memory_usage_bytes{name="my-api"}[10m]) / container_spec_memory_limit_bytes{name="my-api"} > 0.85
Using an averaged window rather than an instantaneous reading for alerting purposes smooths over brief, expected spikes while still catching genuinely sustained elevated usage, which is generally a more reliable alerting signal than a raw, instantaneous percentage threshold.
Percent usage and the OOM kill boundary
It is worth being aware that the percentage reaching 100% does not always precisely correspond to the moment of an OOM kill, since the kernel's accounting and the exact timing of the OOM killer's intervention can introduce small discrepancies between the last observed reading and the actual moment the limit was breached, particularly for fast-moving memory spikes that occur between sampling intervals:
docker inspect my-api --format '{{.State.OOMKilled}}'
Checking the OOMKilled flag directly after an unexpected restart remains the more reliable confirmation of an actual memory-related kill than inferring it purely from the last observed percentage reading before the event.
Common mistakes
- Setting an alerting threshold on memory percent for a container with no explicit memory limit configured, where the denominator is the host's total memory rather than any real, enforced boundary.
- Treating the percentage as purely application memory pressure without accounting for the often substantial page cache component included in the usage figure.
- Alerting on instantaneous percentage readings without smoothing over brief, expected spikes, leading to either excessive false alarms or thresholds tuned too loosely to catch genuine sustained problems.
- Assuming a percentage improvement reflects genuinely reduced memory usage, without checking whether the limit itself was simply raised.
- Relying solely on the last observed percentage reading to confirm an OOM kill, rather than checking the
OOMKilledflag directly.
Stats memory percent is a convenient, quick-glance figure, but its usefulness depends entirely on having an explicit, meaningful memory limit configured as its denominator and on understanding that its numerator includes reclaimable cache alongside genuine application memory, two details that change what a given percentage reading actually implies about a container's real memory health.