mpstat Command: Tutorial & Examples
The per-core x-ray of your processor — which cores are working, which are asleep, and where the time really goes.
What It Is
mpstat shows you what each CPU core is doing, broken out one line per core. Where top blends all your cores into a single %Cpu(s) line and uptime gives you one load average, mpstat pulls them apart so you can see the distribution — and the distribution is exactly where the most common performance mistake hides in plain sight. It's part of the sysstat package, the same toolbox as iostat, pidstat, and sar, and it reads the same plain-text counters the kernel publishes in /proc/stat.
If you've ever stared at top showing one process at 100% while the machine felt fine and wondered what was really going on, mpstat is the tool that answers it in one screen. It only looks, never changes anything, so it's safe to run on anything. We'll learn the reading order first, then explain every column so this doubles as your reference. (Note: mpstat may not be installed by default — on Debian/Ubuntu it's apt install sysstat.)
Your First Look
The most useful form, hands down, is all cores, refreshed once a second:
mpstat -P ALL 1
Linux 6.12.86-amd64 (web01) 06/03/2026 _x86_64_ (8 CPU)
05:03:29 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
05:03:29 PM all 16.37 0.00 3.58 0.26 0.00 0.13 0.00 0.00 0.00 79.67
05:03:29 PM 0 12.12 0.00 2.02 0.00 0.00 1.01 0.00 0.00 0.00 84.85
05:03:29 PM 1 14.29 0.00 5.10 0.00 0.00 0.00 0.00 0.00 0.00 80.61
05:03:29 PM 2 10.31 0.00 2.06 1.03 0.00 0.00 0.00 0.00 0.00 86.60
05:03:29 PM 3 45.36 0.00 2.06 0.00 0.00 0.00 0.00 0.00 0.00 52.58
05:03:29 PM 4 14.43 0.00 4.12 1.03 0.00 0.00 0.00 0.00 0.00 80.41
05:03:29 PM 5 19.19 0.00 4.04 0.00 0.00 0.00 0.00 0.00 0.00 76.77
05:03:29 PM 6 6.19 0.00 5.15 0.00 0.00 0.00 0.00 0.00 0.00 88.66
05:03:29 PM 7 9.18 0.00 4.08 0.00 0.00 0.00 0.00 0.00 0.00 86.73
The header line tells you the kernel, hostname, date, and (8 CPU) — your core count, the same number nproc gives. Then all is the average across every core (this is the number top shows you), and below it, one line per core, numbered 0 through 7. Run it with a trailing 1 and it reprints every second; add a count (mpstat -P ALL 1 5) to take five samples and stop with an Average: block at the bottom.
How I Read It
This is the part top can't do for you, and it takes about two seconds once you know the move.
First I ignore the all line and scan the %idle column down the cores. That single column tells the whole story. If every core has a similar %idle, the load is spread evenly — healthy, the machine is using what it's got. If one core is near 50% idle while the rest sit at 85%+, I've found the thing I came for. Look at core 3 above: 45% busy while its seven neighbours barely break a sweat. That is the signature of a single-threaded program — one process pinned to one core, unable to spread out no matter how many cores you buy it.
Second, if cores are genuinely busy, I read why across the row: is the time in %usr (your application) or %sys (the kernel working on its behalf)? High %sys everywhere usually means syscall-heavy work — lots of tiny I/O, or a network flood. Third I check %iowait and %steal — the two columns that mean "busy, but not with your computation." That's the entire craft: %idle to find the imbalance, %usr vs %sys for the character, %iowait/%steal for the excuses.
The Columns, Explained
Every percentage is a share of that core's time over the interval. They sum to ~100% per row.
CPU— the core number, orallfor the average.mpstat -P ALLgives every core;mpstat -P 3just core 3.%usr— running your programs in user space. The "good" busy — actual application work.%nice— user time spent on deliberately low-priority (niced) processes.%sys— running the kernel on your behalf: system calls, memory management, scheduling. High and steady often means I/O-heavy or context-switch-heavy work.%iowait— the core was idle because it was waiting on the disk. Real, measurable stall time. Sustained%iowaitmeans your bottleneck is storage, not the CPU — chase it withiostat. (Subtle:%iowaitis still technically idle time, just idle-for-a-reason.)%irq/%soft— time servicing hardware and software interrupts. Usually tiny; high%softon one core often means heavy network traffic landing on that core's queue.%steal— the cloud-VM column. Time the hypervisor took your virtual core away to run someone else's. Steady%stealabove a few percent means a noisy neighbour is eating your cycles — your VM is fine, the physical host is overcommitted. This is your evidence to complain or move.%guest/%gnice— time spent running virtual CPUs for guest VMs (only meaningful if this box is a hypervisor).%idle— genuinely doing nothing; spare capacity. The column I read first.
Reading It by Example
One core at 50%, the other seven near idle (the screen above). A single-threaded app. The fix is in the code (or scaling out to more processes), never a bigger box — extra cores just sit idle. This is the single most expensive misunderstanding in server sizing, and mpstat makes it obvious in one glance.
All eight cores at ~90% %usr, low %idle. Genuinely CPU-bound, well-parallelised work using the whole machine. If this is your steady state and things feel slow, you actually do need more cores — one of the rare times that's the right answer.
Every core showing %iowait of 20–40%, low %usr. Not a CPU problem at all. The processor is sitting on its hands waiting for the disk. Stop looking at mpstat and open iostat — this is a storage bottleneck wearing a CPU costume.
%steal parked at 15–25% across cores. You're on an oversubscribed cloud host. Nothing in your code will fix it; the hypervisor is rationing you. Resize, move providers, or open a ticket.
One core pegged with high %soft. Interrupt-heavy network traffic all landing on a single core's queue — the fix is usually enabling multi-queue NICs or IRQ affinity so the load spreads.
Cheat Sheet
mpstat— one-shot average since boot (rarely what you want)mpstat 1— theallaverage, refreshed every secondmpstat -P ALL 1— the one to remember: every core, every secondmpstat -P ALL 1 5— five one-second samples, then anAverage:blockmpstat -P 3 1— watch only core 3mpstat -I SUM 1— total interrupts per second instead of CPU percentagesmpstat -o JSON 1 1— machine-readable output for scripts
How You'll Actually Use It
mpstat -P ALL 1 is the command. You run it the moment top shows a busy machine but you can't tell whether the work is spread out or jammed onto one core — which top's blended line simply cannot answer. Watch it for a few seconds, read the %idle column down the cores, and you instantly know whether you have a parallelism problem (one hot core), a real capacity problem (all cores hot), or a storage problem in disguise (all cores in %iowait). For trend data over hours instead of seconds, its sibling sar records the same numbers to disk.
Gotchas
- The first line is an average since boot. A plain
mpstat(no interval) reports cumulative numbers since the machine started — usually meaningless. Always give it an interval (mpstat -P ALL 1) so each line is a fresh snapshot. %iowaitis per-core and can mislead. On a many-core box, one disk-bound thread can show as low%iowaitspread thin. Pairmpstatwithiostatwhen you suspect the disk.%stealonly appears on virtual machines. Zero%stealon bare metal is normal, not a fix.- Core numbers aren't physical layout. Logical CPU
0–7includes hyperthreads; two "cores" may share one physical core, so two busy siblings aren't quite two cores' worth of work. - Not installed by default. It comes from sysstat (
apt install sysstat/dnf install sysstat).
History & Philosophy
mpstat — the mp is for multiprocessor — comes from Sebastien Godard's sysstat suite, the same lineage as iostat, pidstat, and sar. They all share one philosophy: the kernel is already counting everything in /proc, so a good tool just samples those counters twice, subtracts, and divides by the interval. That's literally all mpstat does — read /proc/stat now, read it again a second later, show you the difference per core.
The reason it earns a place in your toolkit is that it makes one invisible thing visible: a multi-core machine is not one fast CPU, it's eight independent ones, and "busy" is a property of each, not the whole. The day that lands — the day you see seven idle cores next to one pinned core and understand that buying a bigger server won't help — you've graduated from reading averages to reading distributions, and you'll never size a machine naively again.
Column Reference
The per-core columns, each a share of that core's time over the interval:
| Column | Meaning |
|---|---|
%usr |
Running your programs in user space |
%nice |
User time on deliberately low-priority (niced) processes |
%sys |
Running the kernel on your behalf (syscalls, scheduling) |
%iowait |
Idle because the core was waiting on the disk |
%irq |
Servicing hardware interrupts |
%soft |
Servicing software interrupts (high = heavy network traffic) |
%steal |
Time the hypervisor took your virtual core away |
%guest |
Running virtual CPUs for guest VMs (hypervisor hosts only) |
%idle |
Genuinely doing nothing; spare capacity |
See Also
- top — the blended live view
mpstatbreaks apart per core - pidstat — which process is on that hot core
- iostat — when the cores are all stuck in
%iowait - sar — the same numbers, recorded over time
- nproc — how many cores you're reading in the first place
- CPU — cores, threads, and what the percentages really measure
- /proc/stat — the raw counters mpstat samples
Reading
mpstatby hand at 3am isn't a plan.CleverUptime watches every core on every server and tells you in plain words — "one core pinned, your app is single-threaded" or "all cores stalled on disk" — instead of a grid of percentages. See your own server's health in 30 seconds, no signup.