Skip to content
LinuxFix Published Updated 4 min readViews unavailable

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:

Sources:

Comments