Fixing High Load Average on Linux When CPU Usage Looks Normal
Load average climbs while top shows idle CPU — this usually means processes stuck waiting on I/O, not CPU; here's how to find which one.
Linux’s load average counts processes that are either running or waiting to run — including processes blocked waiting on I/O, not just ones actually consuming CPU. A high load average alongside low CPU usage almost always means the second case: something is stuck waiting on disk, network, or another slow resource.
Step 1: confirm this is actually an I/O-wait situation
top
Check the %wa (I/O wait) figure in top’s CPU summary line — a high load average combined with a high %wa and low %us/%sy confirms processes are waiting on I/O rather than genuinely needing more CPU.
Step 2: find processes in uninterruptible sleep
ps aux | awk '$8 ~ /D/ {print}'
Process state D (uninterruptible sleep) specifically means a process is blocked on a kernel-level I/O operation it cannot be interrupted out of — these are exactly the processes inflating load average without touching the CPU.
Step 3: identify what those processes are actually waiting on
cat /proc/<pid>/stack
cat /proc/<pid>/status | grep State
Reading a blocked process’s kernel stack (where permitted) often shows exactly which kernel function it’s stuck in — frequently something disk-I/O-related (wait_on_page_bit, a filesystem journal commit) or network-filesystem-related (an NFS call, similar to FreeBSD’s NFS hard-mount hangs).
Step 4: check overall disk I/O activity system-wide
iostat -x 1
High %util on a specific block device, combined with elevated await (average wait time per I/O request), points at that device being the actual bottleneck — distinguishing “the disk is just slow/overloaded” from “one specific runaway process is issuing excessive I/O.”
Step 5: identify which process is generating the most I/O
iotop
iotop shows per-process disk I/O in something close to real time, which is often the fastest way to identify a single runaway process (a backup job, a misbehaving database query, log rotation gone wrong) responsible for saturating disk I/O for everything else.
Step 6: check for swap thrashing as a distinct cause
vmstat 1
free -h
Heavy swap activity (high si/so columns in vmstat) produces a similar symptom — high load, low CPU usage — but with a different root cause and different fix (more RAM, or reducing memory pressure, rather than addressing disk I/O directly).
Step 7: check for a specific slow storage backend
mount | grep nfs
df -h
If the implicated processes are all touching a network-mounted filesystem specifically, the bottleneck may be the remote storage or the network path to it, not local disk hardware at all.
Why “high load, low CPU” is a specific, well-defined signature worth recognizing immediately
This particular combination is one of the most reliable diagnostic signals Linux offers — it points away from CPU-bound causes (which would show high %us) and directly toward I/O as the bottleneck, letting you skip straight to iostat/iotop rather than wasting time investigating CPU-bound explanations that the load-average-vs-CPU-usage mismatch has already ruled out.
Step 8: use PSI for a more direct signal than load average
cat /proc/pressure/io
Load average was designed decades before cgroups and gives only an indirect, system-wide hint that something is blocked — PSI (Pressure Stall Information), available on kernels 4.20 and newer, is a purpose-built alternative that reports the actual percentage of time some or all tasks spent stalled waiting specifically on I/O, over rolling 10-second, 60-second, and 300-second windows:
some avg10=12.34 avg60=8.21 avg300=3.02 total=93184672
full avg10=4.56 avg60=2.11 avg300=0.87 total=41293841
The some line covers time at least one task was stalled; full covers time every runnable task was stalled simultaneously — a full figure climbing specifically is a much stronger signal of genuine system-wide I/O starvation than load average alone ever provided, since load average conflates “one process is a bit slow” with “the whole system is blocked” in a way PSI’s two separate lines don’t. /proc/pressure/cpu and /proc/pressure/memory offer the identical breakdown for those resources, making PSI a more precise first stop than load average for this entire class of “something feels slow but where” investigation, on any kernel new enough to expose it.
Related:
- How to Monitor System Resources on Linux in Real Time
- Fixing ‘Disk Full’ on Linux When df Shows Space Available
Sources: