⚖️ Load Average Explained

Performance Series: Part 1 — Performance Analysis  |  Part 2 — sar & sysstat  |  Part 3 — Load Average Explained

The Most Misread Number in Linux

Load average is the first number most sysadmins look at when a server feels slow. It is also one of the most frequently misunderstood metrics. A load average of 4.0 might be completely healthy on one server and a serious problem on another — and the reason has nothing to do with how powerful the hardware is.

This page explains what load average actually measures, why the number of CPUs is the essential context for interpreting it, what the three numbers mean, and — critically — why a high load average does not always mean CPU is the bottleneck.

What Load Average Actually Measures

Load average is not CPU utilization percentage. It is the average number of processes that are either:

That third category is the critical one that catches people out. On Linux (unlike older Unix systems), load average includes processes waiting for disk I/O. A disk-saturated server with idle CPUs can have a very high load average.

uptime output: 10:22:15 up 5 days, 2:14, 2 users, load average: 0.52, 0.48, 0.45 │ │ │ │ │ └── 15-minute average │ └────────── 5-minute average └────────────────── 1-minute average
The rule: divide by number of CPUs. A load average of 4.0 on a 4-CPU server = 1.0 per CPU = fully loaded but not overloaded. The same 4.0 on a 1-CPU server = 4x overloaded, processes are queueing. Always find your CPU count first.

Examples

1
Reading Load Average in Context
# View load average
uptime
cat /proc/loadavg

# How many CPUs does this server have?
nproc                          # logical CPU count
lscpu | grep "^CPU(s):"        # detailed CPU info
grep -c processor /proc/cpuinfo

# See load average + CPU count together
echo "CPUs: $(nproc)   Load: $(uptime | awk -F'load average:' '{print $2}')"
Interpreting the same load average differently:
Load average: 3.75, 3.82, 3.90 On a 1-CPU server: 3.75x overloaded -- CRITICAL On a 2-CPU server: 1.88x overloaded -- WARNING On a 4-CPU server: 0.94x loaded -- HEALTHY (slightly under capacity) On a 8-CPU server: 0.47x loaded -- VERY COMFORTABLE On a 16-CPU server: 0.23x loaded -- BARELY TICKING OVER
💡 Load per CPU = load average / nproc. This is the only meaningful way to read load average. Under 1.0 per CPU = capacity available. Around 1.0 per CPU = fully utilized, little headroom. Over 1.0 per CPU = processes queueing, system under pressure. Over 2.0 per CPU = serious problem, investigate immediately.
2
The Three Numbers — Reading the Trend

The three load average numbers are exponentially weighted moving averages over 1, 5, and 15 minutes. Reading them together tells you whether the situation is improving or getting worse:

# Read the trend, not just the current value
uptime
Scenario 1: load average: 0.45, 1.22, 2.88 -- IMPROVING 1-min is much lower than 15-min. The problem was worse 15 minutes ago and is resolving. Scenario 2: load average: 3.44, 2.12, 0.88 -- GETTING WORSE 1-min is much higher than 15-min. Something just started happening. Investigate NOW. Scenario 3: load average: 2.88, 2.92, 2.95 -- SUSTAINED All three numbers are similar. This has been the state for at least 15 minutes. Either normal for this server or a persistent problem. Scenario 4: load average: 0.42, 0.38, 0.41 -- HEALTHY All three low and similar. System is comfortably loaded, stable.
The trend matters more than the number. A load of 3.5 that is falling fast is less concerning than a load of 1.8 that has been climbing steadily for the last 15 minutes. The direction tells you whether to act urgently or monitor.
3
High Load but Idle CPUs — The I/O Trap

This is the scenario that confuses most people. High load average but top shows CPUs are mostly idle:

# High load average but top shows this:
# %Cpu(s):  2.1 us,  0.8 sy,  0.0 ni, 72.3 id, 24.5 wa
#                                      ^^^^^^^^  ^^^^^^^^
#                                      72% idle  24% waiting for I/O

# This is a DISK bottleneck, not a CPU bottleneck
# Processes are stuck waiting for disk I/O (D state)
# They count toward load average even though CPUs are idle

# Verify -- count processes in D state
ps aux | awk '$8 == "D" {print $0}'
ps -eo pid,stat,comm | grep "^.*D"

# How many D-state processes right now?
ps aux | awk '$8 ~ /D/ {count++} END {print count " processes in D state"}'

# Confirm with iostat
iostat -x 1 5   # look for high %util and await on a device

# Find what is doing the I/O
sudo iotop -o
⚠️ High load + high iowait = disk problem, not CPU. Adding more CPU will not help. The fix is to identify the disk bottleneck — a failing disk, a misconfigured RAID, a runaway process writing heavily, or a filesystem that needs attention. Check iostat and iotop, not mpstat.
4
When to Actually Worry
# Calculate load per CPU right now
CPUS=$(nproc)
LOAD=$(awk '{print $1}' /proc/loadavg)
PER_CPU=$(echo "scale=2; $LOAD / $CPUS" | bc)
echo "Load per CPU: $PER_CPU (load=$LOAD, cpus=$CPUS)"

# Thresholds -- these are guidelines not absolute rules
# Per-CPU load:
#  < 0.7  -- comfortable, plenty of headroom
#  0.7-1.0 -- getting busy, monitor closely
#  1.0-2.0 -- overloaded, processes queueing
#  > 2.0  -- seriously overloaded, investigate immediately
#  > 5.0  -- severe, system may become unresponsive

# Also check the run queue directly
vmstat 1 5 | awk 'NR>2 {print "Run queue:", $1, "  Blocked:", $2}'

# r column = processes waiting for CPU
# b column = processes in uninterruptible sleep (disk wait)
# If r > nproc consistently = CPU bottleneck
# If b > 0 consistently = I/O bottleneck
Load per CPUStatusAction
< 0.7✅ ComfortableNo action needed
0.7 – 1.0🟡 Getting busyMonitor, check trend
1.0 – 2.0🟠 OverloadedInvestigate, find bottleneck
2.0 – 5.0🔴 Seriously overloadedImmediate investigation
> 5.0🚨 SevereEmergency — system at risk
5
Load Average History with sar
# View load average history with sar
sar -q

# Load average for a specific time range
sar -q -s 14:00:00 -e 15:00:00

# Load average from a previous day
sar -q -f /var/log/sa/sa25
sar -q output:
10:00:01 AM runq-sz plist-sz ldavg-1 ldavg-5 ldavg-15 blocked 10:10:01 AM 1 245 0.44 0.38 0.41 0 10:20:01 AM 1 246 0.52 0.45 0.42 0 02:10:01 PM 14 312 8.44 6.22 3.18 8 <-- problem 02:20:01 PM 12 318 9.12 7.44 4.22 11 <-- worse 02:30:01 PM 1 248 0.88 2.12 3.44 0 <-- resolving
💡 The blocked column is gold. blocked in sar -q shows processes in uninterruptible I/O wait. At 2:10 PM there were 8 blocked processes alongside a runq of 14 — this was both a CPU and disk I/O problem simultaneously, confirming what the CPU and disk sar data showed.
6
Diagnosing a Zombie Process Problem

Zombie processes appear in the load average discussion but work differently — they don't actually affect load average but they indicate a problem:

# Find zombie processes
ps aux | awk '$8 == "Z" {print $0}'
ps -eo pid,ppid,stat,comm | grep " Z"

# How many zombies?
ps aux | awk '$8 == "Z"' | wc -l

# top shows zombies in the Tasks line:
# Tasks: 185 total,   1 running, 183 sleeping,   0 stopped,   1 zombie

# Find the parent of a zombie (the parent needs to reap it)
ZOMBIE_PID=12345
ps -o ppid= -p $ZOMBIE_PID | xargs ps -p

# Zombies cannot be killed -- they are already dead
# Kill the parent process to clean them up (parent will reap on exit)
# Or send SIGCHLD to the parent to trigger reaping
kill -CHLD $PARENT_PID
Zombies don't consume resources but they do matter. A zombie process has already exited — it is just waiting for its parent to read its exit status. One or two zombies are usually harmless. Hundreds of zombies indicate a buggy application that is not properly waiting for child processes — eventually it will exhaust the process table.
7
High Load Caused by Too Many Threads
# Find processes with very high thread counts
ps -eo pid,nlwp,comm --sort=-nlwp | head -10
# nlwp = number of lightweight processes (threads)

# Count total threads on the system
ps -eo nlwp | tail -n +2 | awk '{total += $1} END {print total " total threads"}'

# Find context switch rate (high cs in vmstat = too many threads)
vmstat 1 5 | awk 'NR>2 {print "Context switches/sec:", $12}'

# View thread details for a specific process
ps -L -p 12345    # list all threads of PID 12345
ls /proc/12345/task/ | wc -l   # count threads

# Java applications are common culprits
ps -eo pid,nlwp,comm | grep java | sort -k2 -rn | head -5
Excessive threads drive up context switches. A Java application with hundreds of threads, a web server with too many workers, or a misconfigured thread pool can cause high load average and high context switch rates even when the work being done is light. The fix is not more hardware — it is tuning the thread count to match the workload.
8
Load Average Monitoring Script
#!/bin/bash
# load-monitor.sh -- alert when load per CPU exceeds threshold

THRESHOLD=2.0    # alert when load per CPU exceeds this
CPUS=$(nproc)
LOAD=$(awk '{print $1}' /proc/loadavg)
LOAD5=$(awk '{print $2}' /proc/loadavg)
LOAD15=$(awk '{print $3}' /proc/loadavg)
PER_CPU=$(echo "scale=2; $LOAD / $CPUS" | bc)

echo "=== Load Average Check: $(hostname) ==="
echo "CPUs:          $CPUS"
echo "Load (1/5/15): $LOAD / $LOAD5 / $LOAD15"
echo "Per CPU:       $PER_CPU"

# Determine status
STATUS=$(echo "$PER_CPU $THRESHOLD" | awk '{if ($1 > $2) print "ALERT"; else print "OK"}')
echo "Status:        $STATUS"

if [[ "$STATUS" == "ALERT" ]]; then
    echo ""
    echo "--- TOP PROCESSES BY CPU ---"
    ps aux --sort=-%cpu | head -6

    echo ""
    echo "--- D-STATE PROCESSES (I/O wait) ---"
    DCOUNT=$(ps aux | awk '$8 ~ /D/ {count++} END {print count+0}')
    echo "D-state count: $DCOUNT"
    ps aux | awk '$8 ~ /D/ {print $0}' | head -5

    echo ""
    echo "--- VMSTAT SNAPSHOT ---"
    vmstat 1 3

    # Send alert email
    echo "High load on $(hostname): $LOAD (per-CPU: $PER_CPU, threshold: $THRESHOLD)" | \
        mail -s "LOAD ALERT: $(hostname)" admin@example.com 2>/dev/null
fi

Quick Reference

CommandWhat it shows
uptimeLoad average (1, 5, 15 min) plus uptime
cat /proc/loadavgRaw load averages + running/total processes
nprocNumber of logical CPUs (divide load by this)
vmstat 1 5r column = run queue, b = blocked on I/O
sar -qHistorical load average + blocked processes
ps aux | awk '$8=="D"'Processes waiting for I/O (D state)
ps aux | awk '$8=="Z"'Zombie processes
ps -eo pid,nlwp,comm --sort=-nlwpProcesses sorted by thread count
top (press 1)Per-CPU breakdown including iowait
iostat -x 1 5Confirm if high load is I/O related

Load Average Decision Tree

What you seeCheck nextLikely cause
High load + low CPU idleps aux --sort=-%cpuCPU bottleneck
High load + high iowaitiostat -x, iotopDisk I/O bottleneck
High load + many D-state processesiotop, iostatDisk bottleneck
High load + high context switchesps -eo nlwp --sort=-nlwpToo many threads
Rising 1-min, stable 15-mintop, ps auxSomething just started
Falling 1-min, high 15-minMonitor onlyProblem resolving

← Back to Performance Index ↑ Back to EXPANDED