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, or all for the average. mpstat -P ALL gives every core; mpstat -P 3 just 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 %iowait means your bottleneck is storage, not the CPU — chase it with iostat. (Subtle: %iowait is still technically idle time, just idle-for-a-reason.)
  • %irq / %soft — time servicing hardware and software interrupts. Usually tiny; high %soft on one core often means heavy network traffic landing on that core's queue.
  • %stealthe cloud-VM column. Time the hypervisor took your virtual core away to run someone else's. Steady %steal above 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 — the all average, refreshed every second
  • mpstat -P ALL 1the one to remember: every core, every second
  • mpstat -P ALL 1 5 — five one-second samples, then an Average: block
  • mpstat -P 3 1 — watch only core 3
  • mpstat -I SUM 1 — total interrupts per second instead of CPU percentages
  • mpstat -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.
  • %iowait is per-core and can mislead. On a many-core box, one disk-bound thread can show as low %iowait spread thin. Pair mpstat with iostat when you suspect the disk.
  • %steal only appears on virtual machines. Zero %steal on bare metal is normal, not a fix.
  • Core numbers aren't physical layout. Logical CPU 07 includes 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 mpstat breaks 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 mpstat by 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.