Why a number above the core count does not automatically mean trouble
Load average shows three exponentially damped averages of the queue of runnable and blocked processes over one, five, and fifteen minutes. Anyone judging the number solely against the core count often confuses CPU-bound with I/O-bound load. This guide explains the calculation, shows how to correctly read uptime and top, and provides clear diagnostic steps for daily practice.
Table of Contents
- 1. What Load Average Actually Measures
- 2. The Three Numbers: Exponentially Damped Averages
- 3. The Run Queue: What the Kernel Actually Counts
- 4. Reading Load Average: uptime, top, and /proc/loadavg
- 5. Load Average and Core Count: When It Gets Critical
- 6. Distinguishing CPU-bound from I/O-bound Load
- 7. Practical Diagnosis with top, vmstat, mpstat, and iostat
- 8. Avoiding Typical Misinterpretations
- 9. Load Average Scenarios Compared
- 10. Summary
- 11. FAQ
1. What Load Average Actually Measures
Load average is one of the oldest and, at the same time, most frequently misunderstood metrics on Linux. It originates from early Unix systems of the 1970s and was originally meant to answer a simple question: how many processes, on average, are waiting to get compute time. The three values in the output of uptime, for example 0.52 0.58 0.59, are not percentages and not CPU utilization in the classic sense, but a smoothed indicator of the length of the queue of runnable and, on Linux additionally, blocked processes.
A common misconception is equating load average with CPU utilization in percent. CPU utilization at 100 percent says nothing about how many additional processes are waiting for compute time, whereas load average captures exactly that. A system can show a high load average at low CPU utilization if many processes are waiting on slow disk I/O. Only understanding this distinction makes the metric usable in practice.
2. The Three Numbers: Exponentially Damped Averages
The three numbers in load average stand for time windows of one, five, and fifteen minutes, but they are not simple arithmetic averages over that period. Instead, the kernel computes an exponentially damped moving average, where more recent measurements are weighted more heavily than older ones. Every five seconds, the scheduler reads the current length of the run queue and updates all three values simultaneously, each with its own time constant for its respective window.
The formula is load(t) = load(t-5s) * exp(-5/60) + n * (1 - exp(-5/60)) for the 1-minute value, with exp(-5/300) for 5 minutes and exp(-5/900) for 15 minutes. In practice this means the 1-minute value reacts quickly to short-term load spikes and drops off just as quickly, while the 15-minute value is sluggish and only rises noticeably after sustained load over a longer period. A 1-minute value clearly above the 15-minute value indicates a spike that has just started, while the reverse pattern indicates a spike that is fading out.
3. The Run Queue: What the Kernel Actually Counts
For the calculation, the Linux kernel adds up two categories of processes: processes in state R (running or runnable), which are either currently running on a CPU or waiting for the scheduler to assign them one, and processes in state D (uninterruptible sleep), which are typically waiting on a blocking I/O operation, such as reading from a disk or an NFS share. This combination distinguishes Linux from classic BSD Unix, where originally only the actual run queue was counted.
This exact inclusion of D-state processes is the reason why a high load average does not automatically mean a CPU bottleneck. A database server waiting on slow network storage can show a load average of 20 even though the CPUs are nearly idle. The number alone does not separate these two causes; it merely signals that processes are blocked or queued, regardless of the reason behind it.
4. Reading Load Average: uptime, top, and /proc/loadavg
The fastest route to load average is the uptime command, which, besides system uptime and the number of logged-in users, prints the three values. The same values appear in the header of top and htop, as well as in plain text form under /proc/loadavg. This file contains five space-separated fields: the three load average values, followed by a fraction such as 2/421 indicating the number of currently running processes relative to the total number of all processes, and finally the PID of the most recently created process.
Context matters when reading these numbers: a single snapshot says little, since short-term spikes are normal. The metric becomes meaningful only as a trend across all three time windows and in comparison with historical values from a monitoring system such as sar or Prometheus node_exporter. Running watch -n 5 uptime regularly quickly gives a feel for how the three values behave under the known load patterns of your own system.
#!/usr/bin/env bash
# Read load average via three different interfaces
uptime
# 14:32:07 up 21 days, 3:47, 2 users, load average: 0.52, 0.58, 0.59
cat /proc/loadavg
# 0.52 0.58 0.59 2/421 18273
# fields: 1min 5min 15min running/total_processes last_pid
# The same numbers appear in the top header, refreshed each interval
top -bn1 | head -1
# top - 14:32:10 up 21 days, 3:47, 2 users, load average: 0.52, 0.58, 0.59
5. Load Average and Core Count: When It Gets Critical
The common rule of thumb is that a load average below the number of logical CPU cores is uncritical, because on statistical average every runnable process gets a CPU immediately. On a system with four cores, a load average of 4.0 is therefore considered fully utilized but still without a queue, whereas a value of 8.0 means that, on average, twice as many processes are runnable or blocked as there are CPUs available.
This rule of thumb is a good starting point, but not a free pass. A brief rise of the 1-minute value above the core count after a cron job or a deployment is usually harmless and normalizes within a few minutes. It only becomes critical when the 15-minute value stays clearly above the core count for a sustained period, because that demonstrably means the queue has been backed up for a longer time. On virtualized systems, the number of actually allocated vCPUs matters as well, not the nominal core count visible inside the guest.
groups:
- name: load-average
rules:
- alert: HighLoadAveragePerCore
# Fires only when the 15-minute average stays above core count
expr: node_load15 / count(node_cpu_seconds_total{mode="idle"}) by (instance) > 1.5
for: 10m
labels:
severity: warning
annotations:
summary: "Load average per core sustained above 1.5 on {{ $labels.instance }}"
description: "Check iowait and steal time before scaling capacity."
6. Distinguishing CPU-bound from I/O-bound Load
The most important practical skill when working with load average is distinguishing CPU-bound from I/O-bound load, because both produce the same number yet require completely different remedies. CPU-bound load means processes are actually consuming compute time and the CPUs are close to 100 percent utilization. I/O-bound load means processes in state D are waiting on an external resource while the CPUs themselves remain largely idle.
The most reliable indicator for telling them apart is the %wa column (I/O wait) in top or vmstat, which shows the fraction of time at least one CPU was idle because a pending I/O operation was waiting to complete. A high %wa value combined with a high load average clearly points to I/O-bound load, while a low %wa value with high CPU utilization indicates genuine compute load. In practice, the command ps -eo pid,stat,cmd filtered on STAT D helps identify the specific processes that are blocking.
7. Practical Diagnosis with top, vmstat, mpstat, and iostat
After the initial assessment via uptime, deeper diagnosis follows with specialized tools. top shows in its %Cpu(s) line the breakdown into us (user), sy (system), id (idle), wa (I/O wait), and st (steal time under virtualization). vmstat 1 delivers, once per second, the size of the run queue in column r and the number of blocked processes in column b, right next to the CPU percentage values, allowing an immediate correlation between load average and actual system behavior.
mpstat -P ALL 1 shows utilization for each individual CPU core and exposes uneven load distribution, for example when a single core is fully saturated by a single-threaded process while other cores remain idle, something the averaged load average conceals. iostat -xz 1 adds the block device level view with the %util column and the average wait time await, showing precisely which device is responsible for the I/O-bound wait time.
#!/usr/bin/env bash
# vmstat: run queue (r) and blocked processes (b) per second
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
# 6 2 0 812340 98212 4213880 0 0 12 40 980 1900 22 6 60 12 0
# mpstat: per-core utilization, exposes single-core saturation
mpstat -P ALL 1 1
# iostat: block device level wait time and utilization
iostat -xz 1 1
# Device r/s w/s await %util
# sda 4.00 12.00 38.20 87.50
8. Avoiding Typical Misinterpretations
A common mistake is assuming a single high load average value automatically means an emergency. Without comparing it against the core count and without distinguishing CPU-bound from I/O-bound causes, this regularly leads to false alarms or unnecessary scaling of resources that do not actually solve the real problem. Equally misleading is the opposite assumption, that a low load average automatically means no problems exist, since short, very intense CPU spikes between the five-second samples are partly absorbed by the exponential smoothing.
On cloud instances, another source of error comes into play: CPU steal time, visible in the %st column of top, arises when the hypervisor cannot deliver promised CPU time due to contention with other tenants. A high load average combined with high steal time is not a problem in your own application but a sign of an overbooked host, and no optimization in your own code can fix that. Only a combination of several metrics, never a single number alone, delivers a reliable diagnosis.
; /etc/sysstat/sysstat - enable historical load and CPU sampling
; so a single snapshot never has to be trusted alone
HISTORY=28
SADC_OPTIONS="-S DISK"
COMPRESSAFTER=10
; /etc/cron.d/sysstat - collect a sample every 5 minutes
; */5 * * * * root /usr/lib/sysstat/sa1 1 1
9. Load Average Scenarios Compared
In practice, it is less the absolute number and more the correct reading of context that determines whether a load average signals a need for action. The following overview contrasts common misinterpretations with the correct diagnostic steps.
| Scenario | Wrong Interpretation | Correct Interpretation | Diagnostic Step |
|---|---|---|---|
| Load of 8.0 sustained on a 4-core system | System is about to crash | Could be CPU-bound or I/O-bound, not clear without more data | Check %wa in top and vmstat 1 |
| 1-minute value jumps to 12 after a cron job | Immediately increase server capacity | Transient spike, watch the 15-minute value over several minutes | Run watch -n 5 uptime |
| Load of 1.0 on a single-core system | System still has capacity headroom | Already fully utilized, no headroom left | Compare nproc against the load value |
| High load on a cloud instance | Equating load 1:1 with physical CPU utilization | Hypervisor contention through steal time possible | Check the %st column in top |
| Low load despite visible CPU spikes | Monitoring is contradictory or broken | Short spikes between 5-second samples get smoothed out | Run mpstat 1 at a one-second interval |
The table shows that every misinterpretation can be resolved with a single additional diagnostic command. Anyone who integrates these steps into a monitoring dashboard no longer has to look things up manually when an alert fires, and instead sees load average, I/O wait, and steal time in the same view.
{
"check": "cpu_load_average",
"host": "shop-prod-02",
"timestamp": "2026-07-12T09:15:00Z",
"cores": 4,
"load_1m": 3.85,
"load_5m": 4.12,
"load_15m": 4.30,
"iowait_percent": 2.1,
"steal_percent": 0.0,
"classification": "cpu_bound",
"status": "warning"
}
Mironsoft
Linux performance analysis and server monitoring for Magento infrastructure
Tired of load average alerts without real diagnosis?
We analyze your Linux servers, distinguish CPU-bound from I/O-bound load, and build monitoring that evaluates load average in the right context instead of alerting on every number above the core count.
Performance Audit
Systematic analysis of load average, I/O wait, and steal time on your production servers
Monitoring Setup
Configuring Prometheus, node_exporter, and sar so thresholds take context into account
Capacity Planning
Separating real bottlenecks from harmless load spikes before scaling unnecessarily
10. Summary
Load average on Linux is an exponentially damped indicator of the number of runnable and blocked processes over one, five, and fifteen minutes, not CPU utilization in percent. The kernel recomputes the three values every five seconds, weighting more recent measurements more heavily than older ones, which makes the 1-minute value react quickly while the 15-minute value reflects trends more robustly. What matters for correct interpretation is comparing the number against the count of available CPU cores and distinguishing CPU-bound from I/O-bound load.
In practice, there is no way around complementary tools like top, vmstat, mpstat, and iostat to correctly assess a high load average. A number above the core count is a signal for further investigation, not an automatic emergency, while a low number can conceal short, intense load spikes. Anyone who looks at load average together with %wa, %st, and the distribution across individual cores makes solid decisions instead of reacting to a single, easily misread number.
CPU Load Average: The Key Points at a Glance
The Three Numbers
1, 5, and 15 minutes as exponentially damped averages of the run queue, recomputed by the kernel every 5 seconds.
What Gets Counted
Processes in state R (runnable) and D (uninterruptible sleep), usually waiting on I/O.
Core Count as a Reference
Load below core count is uncritical, sustained above core count in the 15-minute value is a clear warning sign.
CPU-bound vs. I/O-bound
%wa in top/vmstat distinguishes compute load from I/O wait, %st indicates hypervisor contention.