You log into a Linux box, run top, see CPU at 45%, and assume everything is fine. Two hours later, response times have doubled and an alert finally fires. What did you miss?
CPU utilization on its own is one of the most misread numbers in Linux server monitoring. A box at 45% can be saturated on a single core while seven others sit idle. A box at 90% may be perfectly healthy if it is doing real work and load is steady. Reading Linux CPU monitoring metrics well means knowing which numbers to look at, what each one actually measures, and what threshold should wake you up at 3 a.m.
This guide walks through the core Linux CPU metrics every sysadmin and DevOps engineer should track, the thresholds we recommend, the tools that surface them, and a decision tree for triaging high-CPU alerts when they hit.
Why Linux CPU Metrics Matter
CPU is the cheapest resource to monitor and the most expensive to misread. A single misinterpreted graph can send an on-call engineer chasing the wrong subsystem at midnight while the real bottleneck is on disk or in the hypervisor.
A useful frame is the USE method popularized by Brendan Gregg: for any resource, look at Utilization (how busy it is), Saturation (how much extra work is queued up), and Errors. CPU metrics map cleanly onto this. Utilization tells you what the CPU is doing right now. Saturation — load average, run queue length, context switches — tells you whether more work is waiting than can be processed. Errors are rare on CPU but show up as machine checks in dmesg.
Get the right combination of utilization and saturation metrics under continuous linux server monitoring, and you stop reacting to outages and start preventing them.
The Core Linux CPU Metrics You Should Monitor
These are the metrics worth tracking on every Linux server. Each one tells you something different — together they tell the whole story.
CPU Utilization (us, sy, ni, id, wa, hi, si, st)
When top or mpstat reports CPU usage, it breaks total time into eight buckets. The kernel tracks them in /proc/stat and every monitoring tool ultimately reads from there.
| Field | Name | What it measures |
|---|---|---|
us |
user | Time spent running user-space processes (your apps) |
sy |
system | Time spent in kernel-space (syscalls, scheduling) |
ni |
nice | Time spent on user processes with adjusted priority |
id |
idle | Time the CPU was doing nothing |
wa |
iowait | Time idle while waiting on disk or network I/O |
hi |
hardware IRQ | Time servicing hardware interrupts |
si |
software IRQ | Time servicing softirqs (often network packet processing) |
st |
steal | Time the hypervisor took the CPU away from your VM |
Total CPU usage is 100 - id, but the breakdown is where the diagnostic value lives. High us points at your application code. High sy points at the kernel — excessive forks, system calls, or interrupt storms. High wa is not a CPU problem at all; the CPU is idle, waiting on slow I/O. We'll come back to each of these in the decision tree below.
Load Average (1, 5, 15 minutes)
Load average is the most misunderstood number on a Linux box. It is not CPU usage. On Linux specifically, it counts the number of tasks that are either running on a CPU or waiting in uninterruptible sleep (typically blocked on I/O).
The three numbers are exponentially weighted moving averages over the last 1, 5, and 15 minutes. Read them as a trend:
- 1m > 5m > 15m: load is rising
- 1m < 5m < 15m: load is falling
- All three roughly equal: steady state
The classic rule of thumb is to compare the load average against the number of CPU cores. A 1-minute load of 4 on a 4-core box means the CPU is fully booked. A 1-minute load of 12 on the same box means roughly 8 tasks are queued up waiting their turn — saturation.
$ uptime
14:22:01 up 47 days, load average: 6.42, 3.18, 1.75
If that machine has 4 cores, the trend is bad and the 1-minute number says you're already saturated.
Run Queue Length
Run queue length, reported as r by vmstat, is the number of tasks currently waiting for CPU time. Unlike load average, it excludes tasks blocked on I/O — so it's a cleaner saturation signal for CPU specifically.
$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
8 0 0 1.2G 1.1G 3.4G 0 0 0 0 9.2k 14k 71 6 22 1 0
A persistent r value greater than the core count means tasks are queueing — your CPU has become a bottleneck and adding more capacity (or finding the hot process) is in order. Run queue often spikes before utilization saturates a single core, which makes it a useful leading indicator.
Context Switches and Interrupts
Context switches (cs in vmstat) are the number of times per second the kernel switched from one process or thread to another. Interrupts (in) are hardware and software interrupts the kernel handled.
There is no universal "good" number — both depend heavily on workload. A busy network server can legitimately do 50,000+ context switches per second. What matters is change over time: a sudden spike in context switches without a matching workload change often signals lock contention, misconfigured thread pools, or a runaway process spinning between threads.
Track these as trends. Alert on rate-of-change, not on absolute thresholds.
I/O Wait (iowait)
wa deserves its own callout because it's the metric most often blamed wrongly. High iowait means the CPU is idle, waiting on disk or network. The CPU itself is not the problem — your storage or your network is.
If iowait is consistently above 10–20%, jump straight to disk metrics: queue depth, await time, and IOPS. The fix is almost never "add more CPU." It's almost always "investigate the storage layer" — slow disks, a saturated controller, or an application doing too many small reads.
Steal Time on Cloud and VPS
Steal time (st) is the time your virtual CPU was ready to run but the hypervisor gave that physical core to a different VM. On bare metal, steal time is always zero. On AWS, DigitalOcean, Hetzner Cloud, Vultr, or any other shared virtualization platform, it is one of the most important Linux CPU monitoring metrics you can track.
Two patterns to know:
- Sustained low steal (1–3%) is normal background noise on shared cloud instances. Ignore it.
- Sustained high steal (above 10%) means you're a noisy neighbour victim, you've exhausted a burstable instance's CPU credits (T-series on AWS), or your account is being throttled. Migrate to a dedicated instance type or open a ticket with your provider.
If you run on cloud or VPS infrastructure, alert on steal time. It will catch problems you cannot diagnose any other way.
Recommended Thresholds and Alerts
Knowing the metrics is half the job. Knowing when to alert on them — without drowning your on-call rotation in noise — is the other half.
| Metric | Warning | Critical | Notes |
|---|---|---|---|
CPU utilization (100 - id) |
> 80% sustained 5 min | > 95% sustained 5 min | Per-core; aggregate hides single-thread saturation |
| Load average (1m) | > 1.0 × cores | > 2.0 × cores | Compare against core count, not absolute |
Run queue (vmstat r) |
> cores | > 2 × cores | Sustained 2+ minutes |
iowait (wa) |
> 10% | > 20% | Investigate disk, not CPU |
Steal time (st) |
> 5% | > 10% | Cloud/VPS only; bare metal should be 0 |
| Context switches | > 2× baseline | > 5× baseline | Alert on deviation, not absolute |
Two principles keep alert volume sane:
- Sustained, not spike. A one-second load average of 50 means almost nothing — the box could be running a build. Require the threshold to hold for 2–5 minutes before alerting.
- Per-core for utilization, aggregate for everything else. A single-threaded job pinning one core to 100% will never show up as a problem on an aggregate dashboard. If you only alert on total CPU%, you will miss it every time.
Tools for Collecting Linux CPU Metrics
There are three layers of tooling, each with a different role.
Built-in Commands
Every Linux distribution ships with the basics. Keep these in muscle memory:
top/htop— interactive, real-time view;htopgives you per-core bars with1mpstat -P ALL 1— per-CPU breakdown, refresh every secondvmstat 1— run queue, context switches, interrupts, plus memory and I/Osar -u 1 5— historical and live CPU stats;sarkeeps days of history ifsysstatis enabledpidstat 1— per-process CPU usage over time
These are perfect for live troubleshooting. They are useless for trend analysis, alerting, or post-incident review because they don't persist.
Reading from /proc/stat Directly
If you're writing a custom collector, /proc/stat is the source of truth:
$ cat /proc/stat | head -1
cpu 3429847 1209 891234 87234521 12834 0 4521 8210 0 0
The fields are user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice — in jiffies (typically 1/100th of a second). Sample twice, subtract, and divide to get rates. This is what every monitoring agent does under the hood.
Agent-Based Monitoring
For anything beyond live troubleshooting on a single host, you want an agent. The agent samples metrics on a schedule, ships them to a central system, stores them for trend analysis, and fires alerts against your thresholds. Modern options include Prometheus with node_exporter, the Grafana Agent, Datadog, and our own Xitogent — a lightweight agent that auto-collects every metric in this guide and ships them to Xitoring with no manual configuration.
The agent is what turns CPU monitoring from "I checked when I noticed something" into "I get paged the moment something deviates from normal."
Per-Core vs Aggregate: The Single-Thread Trap
Here's a scenario we see often. An 8-core server runs a legacy report-generation script every hour. The script is single-threaded. While it runs, one core sits at 100% for 12 minutes. Total CPU on the dashboard reads 12.5% — well within any reasonable threshold. No alert fires. But response times for every request that lands on that pinned core spike, and customers complain.
The fix is simple in principle: alert on per-core utilization, not just the aggregate. In practice, that means using mpstat -P ALL for live debugging and configuring your monitoring agent to track per-CPU stats and alert on any single core sustaining > 95%.
This is also why CPU utilization is best paired with run queue length — even when the aggregate hides the problem, a queue forming behind that pinned core gives you a second chance to catch it.
A Decision Tree for High CPU Alerts
When a CPU alert fires at 3 a.m., you want a script you can run in your head. Read the breakdown — us, sy, wa, st — and follow the branch:
-
us(user) is high → it's your application code. Runtopsorted by%CPU, identify the process, thenpidstat -t -p <PID> 1to see which thread. Profile the hot code path. -
sy(system) is high → it's the kernel. Likely culprits: a runaway process forking constantly (pidstat -w 1shows context switches per process), a network interface flooded with packets (checksoftirqtime and/proc/softirqs), or filesystem operations gone wild. -
wa(iowait) is high → it's not CPU, it's storage. Runiostat -x 1. Look at%util,await, and queue depth. The CPU is idle waiting; investigate disks, the storage controller, or whatever the application is reading. -
st(steal) is high → it's the hypervisor. You are on cloud or VPS. Either you've exhausted burstable credits, the host is oversubscribed, or a noisy neighbour is hammering the same physical core. Move workloads to a dedicated instance type or contact the provider. -
All four look normal but load average is high → something is blocked in uninterruptible sleep (D-state). Run
ps -eo state,pid,comm | grep ^D. Almost always disk or NFS related.
Save this table somewhere on-call can find it. It turns a panicked alert into a five-minute investigation.
Monitoring Linux CPU Metrics with Xitoring
If you'd rather not stitch together collectors, dashboards, and alerting yourself, Xitoring does it out of the box. Install Xitogent on any Linux host with a one-line script, and within minutes every metric in this guide — CPU breakdown, load average, run queue, iowait, steal time, per-core utilization — is being collected and graphed.
Threshold alerts ship preconfigured with the values from the table above, and you can route them to Slack, Microsoft Teams, PagerDuty, Opsgenie, Telegram, or any of our other notification channels. When a CPU alert fires, the notification includes the breakdown, so you already know whether to chase user code, the kernel, or a noisy hypervisor before you open the dashboard.
Start a free trial and have continuous Linux CPU monitoring running across your fleet in under five minutes.
FAQs
What is a normal CPU usage on a Linux server?
There is no universal normal. A web server idling at night might sit at 5%; the same server under peak load might run sustained at 70–80% and be perfectly healthy. What matters is whether the workload is being served on time — if response latency is good, high CPU is not a problem. Alert on sustained utilization above 80–95% combined with rising load average and queue length.
What is the difference between load average and CPU usage?
CPU usage is the percentage of time the CPU was busy. Load average is the number of tasks running or waiting to run, including tasks blocked on I/O (uninterruptible sleep). A box can show low CPU usage and high load average if processes are stuck waiting on slow disks or network — that's a load problem, but not a CPU problem.
How do I monitor CPU usage per core on Linux?
The fastest live view is mpstat -P ALL 1. In htop, press 1 to toggle per-core bars. For continuous monitoring, configure your agent to collect per-CPU stats — Xitogent does this by default — and alert on any single core sustaining above 95%.
What does steal time mean on AWS or DigitalOcean?
Steal time is the percentage of time your VM was ready to run but the hypervisor gave the physical CPU to a different VM. On AWS T-series instances it usually means you've burnt through your CPU credits. On any cloud it can mean a noisy neighbour or an oversubscribed host. Sustained steal above 10% is a strong signal to upgrade or migrate.
Which Linux CPU monitoring metrics should I alert on?
At minimum: CPU utilization per core, load average versus core count, iowait, and steal time. Add run queue length if you want a leading indicator before utilization saturates. Use sustained thresholds (5+ minutes) to avoid alert fatigue from transient spikes.
Wrapping Up
Linux CPU monitoring metrics are only useful if you read them in combination. Utilization tells you the CPU is busy. Load average and run queue tell you it's saturated. The user/system/iowait/steal breakdown tells you why — application code, kernel, storage, or hypervisor. Alert thresholds turn the data into action, and the right agent turns the action into a graph and a notification before customers notice.
If you're ready to put continuous CPU monitoring in place across your Linux fleet, see how Xitoring's linux server monitoring does it, or read more on why metric monitoring matters for uptime and server monitoring best practices.
