| Tested on | Red Hat Enterprise Linux 10.2 (Coughlan) |
|---|---|
| Package | podman-5.8.2-5.el10_2.x86_64 |
| Applies to | Linux hosts with Podman where you need live or point-in-time CPU, memory, PID, and I/O utilization for containers or pods |
| Privilege | Rootful examples on the lab host; rootless cgroup and network-stat caveats noted where behavior differs |
| Scope | podman stats and podman pod stats, default streaming and --no-stream, --all, --format Go templates and JSON, column meanings, CPU and memory interpretation, PIDs, podman top with host PID descriptors, cgroup version checks, reading cgroup v2 memory.current, cpu.stat, and pids.current, rootless cgroup v1 failure, and rootless network I/O limitations. Does not cover setting CPU or memory limits, Prometheus or Grafana, or generic Linux performance tuning. |
| Related guides | Inspect Podman containers List containers with podman ps View and follow container logs Podman pods Podman architecture |
podman ps tells you a container is running. podman stats tells you how hard it is working — CPU percent, memory footprint, process count, and I/O counters pulled from cgroups. This guide focuses on reading those numbers on Podman 5.8.2 and cross-checking them against cgroup v2 files on the host.
Setting limits belongs in the resource-limits guide; here you only measure what the workload already consumes.
Lab setup
Create a working directory and a container that keeps one CPU busy without exhausting the host:
mkdir -p ~/podman-stats-lab && cd ~/podman-stats-labStart a detached container that repeatedly reads from /dev/zero so CPU stays busy:
podman run -d --name stats-demo docker.io/library/alpine:latest sh -c 'i=0; while true; do i=$((i+1)); dd if=/dev/zero of=/dev/null bs=1M count=10 2>/dev/null; done'Add a mostly idle container for contrast:
podman run -d --name stats-idle docker.io/library/alpine:latest sleep 3600Give stats-demo a few seconds to warm up before you read stats.
Start with podman stats on one container
Stream live utilization for a single target (press Ctrl+C to stop):
podman stats stats-demoFor documentation, capture one snapshot with --no-stream (covered in depth later):
podman stats --no-stream stats-demoSample output on this host:
ID NAME CPU % MEM USAGE / LIMIT MEM % NET IO BLOCK IO PIDS CPU TIME AVG CPU %
064bd4dcfd35 stats-demo 93.36% 827.4kB / 8.052GB 0.01% 1.955kB / 628B 0B / 0B 2 11.29544s 93.36%Column meanings on Podman 5.8.2:
- ID — short container ID
- NAME — container name
- CPU % — CPU utilization for the sampling window
- MEM USAGE / LIMIT — cgroup memory use versus limit
- MEM % — memory use as a percent of the limit column
- NET IO — received / transmitted network bytes
- BLOCK IO — read / write block bytes
- PIDS — process count in the cgroup
- CPU TIME — cumulative CPU time
- AVG CPU % — average CPU utilization over the container's observed lifetime rather than only the latest sampling interval; it may initially match CPU % but can diverge as workload changes
An idle-only example would show ~0% CPU and one PID — stats-idle fills that role in the comparisons below.
Monitor all running containers
Without arguments, podman stats watches every running container:
podman stats --no-streamSample output:
ID NAME CPU % MEM USAGE / LIMIT MEM % NET IO BLOCK IO PIDS CPU TIME AVG CPU %
05212f2a4518 stats-idle 0.06% 380.9kB / 8.052GB 0.00% 7.612kB / 698B 0B / 0B 1 12.707ms 0.06%
064bd4dcfd35 stats-demo 92.83% 823.3kB / 8.052GB 0.01% 1.955kB / 628B 0B / 0B 2 11.430495s 92.83%Stopped containers are omitted by default. Include them with --all:
podman stats --no-stream --allWhen every listed container is already running, --all matches the default output. The flag matters when you want stats rows for created or exited containers too.
Get a one-time snapshot with --no-stream
Scripts should almost always disable the live refresh loop:
podman stats --no-streamTarget specific containers by name:
podman stats --no-stream stats-demo stats-idleWithout --no-stream, Podman redraws the table continuously — fine for troubleshooting, awkward for cron jobs or CI parsers.
Format podman stats output
Pull selected fields with a Go template:
podman stats --no-stream --format '{{.Name}} {{.CPU}} {{.MemUsage}}' stats-demo stats-idleSample output:
stats-demo 89.03190481909083 577.5kB / 8.052GB
stats-idle 0.05968412826535389 380.9kB / 8.052GB.CPU is a floating-point value, not a pre-formatted percent string. Other useful placeholders include .MemPerc, .NetIO, .BlockIO, and .PIDs.
Emit JSON for tools that already speak JSON:
podman stats --no-stream --format json stats-demoSample output (trimmed):
[
{
"id": "064bd4dcfd35",
"name": "stats-demo",
"cpu_percent": "88.37%",
"mem_usage": "995.3kB / 8.052GB",
"mem_percent": "0.01%",
"net_io": "1.955kB / 628B",
"block_io": "0B / 0B",
"pids": "2"
}
]JSON field names use snake_case (cpu_percent, mem_usage, net_io). You do not need every template key memorized — start from --format json once, then trim.
Understand CPU percentage
Check how many logical CPUs the host exposes:
nprocSample output:
3Compare idle versus busy containers:
podman stats --no-stream --format '{{.Name}} {{.CPU}}' stats-idle stats-demoSample output:
stats-idle 0.033
stats-demo 86.84On this three-core host, a single hot loop in stats-demo reported roughly 87–94% CPU, which is close to saturating one logical CPU. Podman CPU percentages are per-core style: a container using two cores concurrently can approach 200%, and a workload capable of using all three cores on this host could approach 300%. stats-idle stayed near zero.
To see which process burns CPU inside the container, use podman top in the next section — stats alone does not name the command.
Understand memory usage
Memory columns tie directly to cgroup accounting:
podman stats --no-stream stats-demoSample output:
064bd4dcfd35 stats-demo 92.83% 823.3kB / 8.052GB 0.01% ...- MEM USAGE — current cgroup memory consumption
- MEM LIMIT — effective ceiling (
8.052GBhere with no--memoryflag set) - MEM % — usage divided by the limit column
With no explicit --memory on the container, the limit reflects the cgroup hierarchy's effective cap — on this host it matched available host memory while memory.max in cgroup v2 read max (no hard cgroup limit). Do not assume the limit column always equals installed RAM without checking your cgroup tree.
Monitor process count (PIDS)
Spawn background shells inside the busy container:
podman exec stats-demo sh -c 'for i in 1 2 3 4 5; do sleep 3600 & done'Read the updated PID count:
podman stats --no-stream --format '{{.Name}} {{.PIDs}}' stats-demoSample output:
stats-demo 7The PIDS column rose from 2 to 7 after five background sleep processes joined the cgroup. Cross-check with podman top when you need process names, not just a count.
Use podman top
List processes as the container sees them:
podman top stats-demoSample output:
USER PID PPID %CPU ELAPSED TTY TIME COMMAND
root 1 0 4.602 21.729155096s ? 1s sh -c i=0; while true; do i=$((i+1)); dd if=/dev/zero of=/dev/null bs=1M count=10 2>/dev/null; donePodman adds descriptors that map container IDs to host IDs — especially useful rootless:
podman top stats-demo pid hpid user huser pcpu argsSample output (trimmed):
PID HPID USER HUSER %CPU COMMAND
1 323627 root root 4.384 sh -c i=0; while true; do ...Descriptor meanings:
- pid — PID inside the container namespace
- hpid — corresponding PID on the host
- user — user inside the container
- huser — user as resolved on the host
A quick capability glance without turning this into a security audit:
podman top stats-demo pid seccomp capeff argsSample output (trimmed):
PID SECCOMP EFFECTIVE CAPS COMMAND
1 filter CHOWN,DAC_OVERRIDE,FOWNER,FSETID,KILL,NET_BIND_SERVICE,SETFCAP,SETGID,SETPCAP,SETUID,SYS_CHROOT sh -c i=0; while true; do ...podman top is a snapshot; podman stats streams cgroup totals. Use both when monitoring intent needs process names and resource percentages together.
Monitor pod resources with podman pod stats
Pods expose per-member statistics — not a single aggregate row:
podman pod create --name stats-podAdd a tight loop as the pod workload:
podman run -d --pod stats-pod --name stats-pod-worker docker.io/library/alpine:latest sh -c 'while true; do :; done'After the worker warms up, read pod stats once:
podman pod stats --no-stream stats-podSample output:
POD CID NAME CPU % MEM USAGE/ LIMIT MEM % NET IO BLOCK IO PIDS
7eae5b1cc3d8 a46ad33e8354 7eae5b1cc3d8-infra 0.04% 213kB / 8.052GB 0.00% 6.876kB / 698B -- / -- 1
7eae5b1cc3d8 9b021cbefaee stats-pod-worker 90.98% 426kB / 8.052GB 0.01% 6.876kB / 698B -- / -- 1Each line is one pod member — infra container plus workload containers. BLOCK IO may show -- / -- for pod stats even when container stats show 0B / 0B.
Check cgroup version first
Before reading cgroup files, confirm what Podman uses on the host:
podman info --format '{{.Host.CgroupsVersion}}'Sample output on RHEL 10.2:
v2Cgroup manager:
podman info --format '{{.Host.CgroupManager}}'Sample output:
systemdModern RHEL and Fedora hosts report v2 with systemd. Older distributions on cgroup v1 behave differently — especially rootless stats, covered next.
Rootless cgroup v1 limitation
podman stats does not work for rootless Podman on cgroup v1. Podman cannot read the cgroup statistics rootless cgroup v1 exposes reliably, so the command fails or returns empty data on those hosts.
RHEL 10 uses cgroup v2 by default, so this mostly affects older installations. This guide does not walk through enabling cgroup v1 — migrate the host or run rootful stats where policy allows.
Rootless network statistics caveat
Even on cgroup v2, Podman documents incomplete network I/O accounting for rootless containers — NET IO may read 0B / 0B or stay misleadingly low despite real traffic. Do not treat a zero NET IO column as proof the network is down.
On a rootless cgroup v2 test here (after enabling user linger), a tiny container still reported 284B / 902B NET IO. Other rootless networking stacks may not populate the column. Verify connectivity with application-level checks when NET IO looks wrong.
Find the container cgroup path
Start from the container init PID on the host:
podman inspect --format '{{.State.Pid}}' stats-demoSample output:
323627Follow the cgroup membership file for that PID:
cat /proc/323627/cgroupSample output:
0::/machine.slice/libpod-064bd4dcfd356746088660040513f6e71f37772a882db3b2930c94137f39b319.scope/containerThe path after 0:: maps under /sys/fs/cgroup/ on cgroup v2. Paths differ for rootless users, Quadlet units, and pods — always discover the path from the running container instead of hard-coding a directory.
systemd-cgls can help locate the same scope in the systemd hierarchy when you think in units rather than /proc paths.
Read memory from cgroup v2
With the path above, read current and maximum memory (replace CGROUP_PATH with your discovered path):
cat /sys/fs/cgroup/machine.slice/libpod-064bd4dcfd356746088660040513f6e71f37772a882db3b2930c94137f39b319.scope/container/memory.currentSample output:
2387968The hard limit for the same cgroup — max means no cgroup-specific cap was set:
cat /sys/fs/cgroup/machine.slice/libpod-064bd4dcfd356746088660040513f6e71f37772a882db3b2930c94137f39b319.scope/container/memory.maxSample output:
max- memory.current — bytes in use by the cgroup now
- memory.max — hard limit;
maxmeans no cgroup-specific cap
2387968 bytes is about 2.3 MiB — the same order of magnitude as the ~800kB–3.5MB MEM USAGE Podman reported across snapshots. Small gaps are normal because stats sampling and memory.current reads happen at slightly different times.
Read CPU statistics from cgroup v2
CPU accounting lives in cpu.stat:
cat /sys/fs/cgroup/machine.slice/libpod-064bd4dcfd356746088660040513f6e71f37772a882db3b2930c94137f39b319.scope/container/cpu.statSample output (trimmed):
usage_usec 25175822
user_usec 12446422
system_usec 12729399
nr_throttled 0
throttled_usec 0usage_usec is total CPU time in microseconds. throttled_usec stays zero until a CPU quota throttles the cgroup — when you set CPU limits, compare cpu.max and throttling fields against Podman resource limits.
Read PID limits and usage
Process count from cgroup files:
cat /sys/fs/cgroup/machine.slice/libpod-064bd4dcfd356746088660040513f6e71f37772a882db3b2930c94137f39b319.scope/container/pids.currentSample output:
7Maximum allowed:
cat /sys/fs/cgroup/machine.slice/libpod-064bd4dcfd356746088660040513f6e71f37772a882db3b2930c94137f39b319.scope/container/pids.maxSample output:
2048pids.current matched the PIDS column (7) after the background sleep processes were started. Setting a lower cap belongs in the resource-limits guide.
podman stats vs podman top
| Topic | podman stats |
podman top |
|---|---|---|
| Focus | cgroup resource totals | per-process list |
| Data | CPU %, memory, I/O, PID count | PID, user, command, capabilities |
| Mode | streaming by default; --no-stream for snapshots |
one-shot snapshot |
| Best for | capacity and saturation checks | finding which process is hot |
Both answer monitoring questions from different angles — totals versus process detail.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
podman stats empty or errors rootless |
cgroup v1 host | Upgrade to cgroup v2 or use rootful Podman |
NET IO 0B / 0B rootless |
documented network accounting gap | verify with app traffic; do not assume network failure |
| CPU % near 100% on one busy loop | one core saturated on multi-core host | expected; compare with nproc and podman top |
| MEM LIMIT shows host RAM | no --memory set |
read memory.max in cgroup; set limits if you need a cap |
| PIDS mismatch | short-lived children | re-run --no-stream; use podman top for live list |
cgroup path not under machine.slice |
rootless, Quadlet, or pod layout | discover via /proc/PID/cgroup each time |
References
- podman-stats(1) — flags, formatting, and streaming behavior
- podman-top(1) — process descriptors including
hpidandhuser - podman-pod-stats(1) — pod-level statistics
- Red Hat — Building, running, and managing containers — monitoring containers on RHEL-family systems
Summary
podman stats is the first tool for container capacity checks — CPU percent, memory use against the effective limit, process count, and I/O counters updated from cgroups. Use --no-stream in scripts and --format when you need JSON or selected columns without parsing a live table.
On this three-core host, one busy loop reported roughly 90% CPU while an idle neighbor stayed near zero. Memory numbers align with cgroup v2 memory.current when you discover the path through podman inspect and /proc/PID/cgroup. podman pod stats lists each pod member separately, including the infra container — not one combined row.
Rootless cgroup v1 cannot support stats at all. Rootless cgroup v2 may show incomplete NET IO even when traffic flows. Pair stats with podman top when you need host PIDs and command lines, and with cgroup files when you need kernel-level confirmation. To cap what a container may consume, move on to resource limits — this page only measures current use.

