High load and a slow server: load vs CPU, iowait, D-state

A server feels slow and the first number everyone looks at is the load average. The trap is reading it on its own: a Linux high load average can mean the CPUs are flat out, or it can mean they are almost idle while tasks sit waiting for a disk or a stuck NFS mount. Treat every high load as “needs more CPU” and you will order hardware that changes nothing.

Here you’ll learn what load average actually counts, why uninterruptible (D state) tasks push it up without using any CPU, and how to tell a CPU-bound server from a disk-bound one with vmstat, top, iostat and pidstat. You’ll create both kinds of load with stress-ng, find the D-state processes behind a stall, see how an NFS hang drives load up, and spot steal time on a busy hypervisor.

Where this lesson fits

Lesson 73 of 82 in Linux Administration · Phase 4: Troubleshooting step by step · Before this: Lesson 72, can’t SSH into the server · Next: Lesson 74, out of memory and the OOM killer

TimeYou needBy the end you can
About 75 minutesrocky01 (2 vCPU, 2 GB RAM) with an SSH session as student; ubu01 works the same, and the NFS mount from Lesson 47 for the hang exampleRead load against the CPU count, tell CPU-bound from I/O-bound in two minutes, find D-state processes and the device behind them, and recognise an NFS hang and steal time

What it is, in one line

Load average is the number of tasks running or waiting to run plus tasks stuck in uninterruptible sleep, so high load means “something is queued”, and the next step is to find out whether that something wants a CPU or is waiting for a disk.

In Simple Words

The clinic with two doctors

A small clinic has two doctors. One morning fifteen people are inside: four are being seen (two doctors, two more in side rooms), some are in the queue, and a few are just sitting because their blood test hasn’t come back from the lab yet and the doctor can’t do anything until it does.

If you only count heads at the door, you panic and say “hire more doctors”. But the doctors might be sitting idle, waiting on the slow lab. A third doctor wouldn’t help the people stuck waiting for test results.

Load average is the head count, the doctors are CPUs, and the people waiting on the lab are tasks stuck waiting for the disk.

Engineer's View

What load average counts, and what it hides

Load average, from /proc/loadavg, is the average number of tasks in state R (running or queued for a CPU) plus state D (uninterruptible sleep, nearly always waiting on disk or NFS). The three figures are weighted averages over 1, 5 and 15 minutes. Because D-state tasks count, a server with a stuck mount can show a load of 20 while its CPUs are almost idle, which is the single most misread number in Linux.

Load means nothing until you divide it by the CPU count from nproc. On rocky01 with 2 vCPUs, a load of 2 means both are fully used with nobody waiting; 4 means tasks queue for half their time. On a 64-core box, 20 is a quiet afternoon.

So the real question is never “is load high” but “is this CPU work or waiting”, and these columns answer it:

Tool and columnPoints at CPUPoints at I/O (disk/NFS)
vmstat 1r (run queue) above the CPU count, high us + syb (blocked) above 0, high wa
tophigh %Cpu us, tasks in R statehigh %Cpu wa, tasks in D state
iostat -xz 1disks quiet, low %utilhigh r_await / w_await, aqu-sz, %util
pidstat 1 / -d 1%CPU per processkB_wr/s, iodelay per process
ps -eo statR processesD processes (can’t be killed)

Two more culprits hide here. On a VM, steal time (st in vmstat, %steal in sar) is time your vCPU was ready but the hypervisor gave the physical core to another guest, so load climbs and the fix is on the host, not in your VM. And a D-state task waiting on an NFS server that stopped answering cannot be killed (not even with kill -9) until the server returns or the mount is forced down, so one dead NFS export can hang every process that touches it and drive load through the roof.

How it works, step by step

A slow-server ticket runs down a short checklist: how high is load against the CPUs, is it CPU or I/O, which device, which process. The diagram below shows it. First, read load the right way.

1Load counts R and D tasks

Every few seconds the kernel counts tasks in state R and state D and folds that into the three averages. A 1-minute value of 4.00 on a 2-CPU box means that, on average over the last minute, four tasks wanted a CPU or were stuck waiting on storage. The 1-minute number reacts fast; the 15-minute one tells you if it’s a spike or a long problem.

2Split CPU work from waiting

vmstat 1 prints one line a second. r is tasks wanting a CPU, b is tasks blocked in D state, and the cpu columns are us, sy, id, wa, st. If us + sy is near 100 and r is bigger than your CPU count, the CPUs are the limit. If id + wa is high and b is above zero, the CPUs are waiting for storage and more CPU would do nothing.

Linux high load average: the checklist from load to CPU-or-I/O to device to process

3Find the slow device with iostat

iostat -xz 1 shows each busy disk once a second. The numbers that matter are r_await and w_await (milliseconds per request including queue time) and aqu-sz (average requests waiting). %util is the share of time the device was busy, honest for one spinning disk but misleading on SSD, NVMe or a SAN LUN that serves many requests at once, so trust await and aqu-sz first.

4Catch the D-state processes

While the stall is happening, ps -eo stat,pid,user,cmd | awk '$1 ~ /^D/' lists the tasks in uninterruptible sleep, and cat /proc/PID/stack or /proc/PID/wchan shows what kernel function they are blocked in (a disk wait, an NFS call). pidstat -d 1 then tells you which process is doing the I/O. The picture below maps the two shapes of a high-load incident.

5Rule out the hypervisor and NFS

If the CPUs look busy but your own processes don’t add up, check st in vmstat: steal time above a few percent means a noisy neighbour on the VM host. And if D-state tasks are blocked in NFS calls (wchan shows something like rpc_wait_bit_killable) and the mount won’t respond, the server that exports it is the problem; those tasks won’t die until it comes back. The diagram below shows where each one sits.

Linux high load average: CPU-bound versus I/O-bound shapes, plus steal and NFS

6Decide the fix from the evidence

CPU-bound with a real busy process: find it with pidstat or top and deal with that workload (a runaway job, too few CPUs for real demand). I/O-bound: find what’s writing or reading and slow or move it, or fix the storage. Steal: talk to the virtualisation team.

NFS hang: recover or hard-unmount the export.

Load average is a doorbell, not a diagnosis. It tells you to look; vmstat, iostat and pidstat tell you what to fix. Lesson 53 built these tools up as everyday performance monitoring; here they are the toolkit for the “why is it slow right now” page.

Hands-on lab: CPU-bound vs I/O-bound load on rocky01

Before you run any command, double-check it

Read every command twice before you press Enter. Check the target: the server name, the user, the disk or device name, the path and the sizes. These commands are for learning and were tested in our lab, not on your system. At work, run the read-only commands first, follow your change process, make sure a backup exists, and ask a second person to check anything that changes or deletes data. You run them at your own risk.

Everything runs on rocky01 as student, in ~/lab73. stress-ng stops by itself after its timeout, so nothing keeps running if you walk away. You met these tools in Lesson 53; here you use them to decide CPU versus I/O on the spot. Take a snapshot first (VBoxManage snapshot rocky01 take before-l73) and start with mkdir -p ~/lab73 && cd ~/lab73.

1Make sure the tools are there

Run this (they were installed in Lesson 53; this adds them if not):

sudo dnf install -y sysstat stress-ng
nproc
uptime
cat /proc/loadavg

You should see something like this:

 11:02:14 up  1:48,  2 users,  load average: 0.04, 0.08, 0.03
0.04 0.08 0.03 1/164 2740
2

On Rocky Linux 10 both packages come from the normal AppStream repository, so no EPEL is needed; on ubu01 it’s sudo apt install sysstat stress-ng from universe. rocky01 has 2 vCPUs, so a load near 2 means “fully busy” and anything well above 2 means tasks are queued. Right now load is near zero. In /proc/loadavg the fourth field (1/164) is running tasks over total tasks.

✅ Checkpoint: You know rocky01 has 2 CPUs, so you know 2.00 is the “fully busy” load for this box.

2Read a resting baseline with vmstat

Run this (one line a second, three lines):

vmstat 1 3

You should see something like this:

procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st gu
 0  0      0 1051220   4908 380240    0    0    21    14  118  160  1  0 99  0  0  0
 0  0      0 1051220   4908 380240    0    0     0     0   96  141  0  0 100 0  0  0
 0  0      0 1051216   4908 380240    0    0     0    12  101  150  0  1 99  0  0  0

Ignore the first line, which is the average since boot. On a quiet box r is 0 or 1, b is 0, id is 99 to 100, and wa and st are 0. Remember this shape: everything that follows is a change from it.

✅ Checkpoint: Your live vmstat lines show r 0-1, b 0, id near 100 and wa 0.

3Make it CPU-bound and read the shape

Start four CPU workers (twice the CPU count) for two minutes, wait a minute, then look:

stress-ng --cpu 4 --timeout 120s &
sleep 60
uptime
vmstat 1 3

You should see something like this:

[1] 2803
stress-ng: info:  [2803] setting to a 2 mins run per stressor
stress-ng: info:  [2803] dispatching hogs: 4 cpu
 11:08:44 up  1:54,  2 users,  load average: 2.78, 0.96, 0.41
procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st gu
 4  0      0 1022140   4908 380360    0    0     0     0 1052  534 99  1  0  0  0  0
 5  0      0 1022140   4908 380360    0    0     0    12 1061  540 99  1  0  0  0  0
 4  0      0 1022140   4908 380360    0    0     0     0 1044  523 100 0  0  0  0  0

Load climbed past 2 towards 4, and the shape is clear: r is 4 or 5 (tasks wanting a CPU), us is 99 to 100, and b and wa are 0. This is CPU-bound. More vCPUs would genuinely help here, because tasks are queued for the CPU, not waiting on anything.

Confirm with top and pidstat:

top -b -n 1 | head -n 12
pidstat -u 1 1 | head -n 8

You should see something like this:

top - 11:08:52 up  1:54,  2 users,  load average: 3.41, 1.28, 0.54
Tasks: 139 total,   5 running, 134 sleeping,   0 stopped,   0 zombie
%Cpu(s): 98.7 us,  1.3 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   1742.2 total,    999.1 free,    328.0 used,    415.1 buff/cache
MiB Swap:   2048.0 total,   2048.0 free,      0.0 used.   1414.2 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   2805 student   20   0   78424   5632   3456 R  99.0   0.3   0:42.11 stress-ng-cpu
   2806 student   20   0   78424   5632   3456 R  98.7   0.3   0:42.08 stress-ng-cpu
   2807 student   20   0   78424   5632   3456 R  49.3   0.3   0:21.02 stress-ng-cpu
   2808 student   20   0   78424   5632   3456 R  49.0   0.3   0:20.98 stress-ng-cpu

Linux 6.12.0-211.16.1.el10_2.0.1.x86_64 (rocky01) 	10/27/2026 	_x86_64_	(2 CPU)

11:09:10 AM   UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
11:09:11 AM  1000      2805   99.00    1.00    0.00    0.00  100.00     0  stress-ng-cpu
11:09:11 AM  1000      2806   99.00    0.00    0.00    1.00   99.00     1  stress-ng-cpu
11:09:11 AM  1000      2807   49.00    0.00    0.00   50.00   49.00     0  stress-ng-cpu
11:09:11 AM  1000      2808   50.00    0.00    0.00   49.00   50.00     1  stress-ng-cpu

top shows tasks in the R state and %Cpu wa at 0, and pidstat names the four stress-ng workers. With four workers on two CPUs, two run at about 99% and two at about 50% with 50% %wait, because they take turns. Stop it early with kill %1 if you don’t want to wait for the timeout.

✅ Checkpoint: vmstat showed r 4-5, us near 100 and wa 0, and pidstat named the stress-ng-cpu workers.

Linux high load average lab: vmstat showing blocked tasks and iowait on rocky01
Example output: vmstat on rocky01 showing blocked tasks and high iowait during a disk-bound load.

4Make it I/O-bound and see the difference

Wait until the CPU test has ended, then start two disk writers that sync every write:

stress-ng --hdd 2 --hdd-opts dsync --hdd-bytes 256M --temp-path ~/lab73 --timeout 180s &
sleep 45
uptime
vmstat 1 3

You should see something like this:

[1] 2851
 11:15:31 up  2:01,  2 users,  load average: 1.74, 0.71, 0.49
procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st gu
 0  3      0 1013904   4908 384508    0    0     0  3464 2210 3105  1  5 44 50  0  0
 0  4      0 1013904   4908 384508    0    0     0  3452 2198 3087  0  6 43 51  0  0
 1  3      0 1013904   4908 384508    0    0     0  3480 2231 3122  1  5 44 50  0  0

Load is climbing again, but the shape is the opposite: r is 0, b is 3 or 4 (tasks blocked on I/O), us is about 1, and wa is 50. The CPUs are half idle and half waiting for the disk, yet the load average looks just as alarming as the CPU test. This is the case where adding CPUs changes nothing.

Look at the disk and the CPU split:

top -b -n 1 | grep '%Cpu'
iostat -xz 1 2

Read the second iostat report. You should see something like this:

%Cpu(s):  0.8 us,  5.6 sy,  0.0 ni, 43.2 id, 50.4 wa,  0.0 hi,  0.0 si,  0.0 st

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           0.50    0.00    5.53   50.25    0.00   43.72

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz     f/s f_await  aqu-sz  %util
dm-0             0.00      0.00     0.00   0.00    0.00     0.00  866.00   3464.00     0.00   0.00    9.21     4.00    0.00    0.00    7.98  99.60
sda              0.00      0.00     0.00   0.00    0.00     0.00  866.00   3464.00     0.00   0.00    4.83     4.00  842.00    2.14    5.99  99.60

top confirms wa near 50 with us tiny. iostat shows sda and the dm-0 volume above it busy: each write is 4 KiB (wareq-sz), about 866 a second, with w_await 4.8 ms on sda and 9.2 ms through LVM, and aqu-sz of 6 to 8 requests queued. On this single VirtualBox disk %util 99.6 does mean full, but on SSD or SAN trust await and aqu-sz first.

✅ Checkpoint: vmstat showed b above 0 and wa near 50 while us stayed low, and iostat showed w_await and aqu-sz above zero for sda.

5Catch the D-state processes behind the stall

While the disk test is still running, find the uninterruptible tasks and what they wait on:

ps -eo stat,pid,user,wchan:20,cmd | awk '$1 ~ /^D/'
pidstat -d 1 1 | head -n 6
P=$(pgrep -f stress-ng-hdd | head -n1); sudo cat /proc/$P/stack

You should see something like this:

D+      2853 student  submit_bio_wait      stress-ng-hdd [run]
D+      2854 student  folio_wait_bit       stress-ng-hdd [run]
D+      2856 student  submit_bio_wait      stress-ng-hdd [run]

Linux 6.12.0-211.16.1.el10_2.0.1.x86_64 (rocky01) 	10/27/2026 	_x86_64_	(2 CPU)

11:15:44 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
11:15:45 AM  1000      2853      0.00   1736.00      0.00      18  stress-ng-hdd
11:15:45 AM  1000      2854      0.00   1728.00      0.00      17  stress-ng-hdd

[<0>] folio_wait_bit_common+0x124/0x340
[<0>] folio_wait_writeback+0x2b/0x80
[<0>] __filemap_fdatawait_range+0x8a/0x100
[<0>] file_write_and_wait_range+0x8e/0xd0
[<0>] xfs_file_fsync+0x5c/0x240 [xfs]
[<0>] do_fsync+0x3d/0x80

The D in the first column is uninterruptible sleep: these tasks are stuck in a kernel call and even kill -9 won’t touch them until the I/O finishes. wchan and /proc/PID/stack show where: here it’s a file write and fsync waiting on the disk. pidstat -d names the writers and their kB/s. This is the evidence that turns “load is high” into “these two processes are hammering the disk”.

If /proc/PID/stack says Permission denied, you ran it without sudo: only root can read a kernel stack. Stop the test with kill %1 when done.

✅ Checkpoint: You listed D-state stress-ng-hdd tasks, saw a disk-wait function in wchan or the stack, and pidstat -d named the writers.

6See an NFS hang drive load up

This uses the NFS mount from Lesson 47 (ubu01 exports /srv/nfs/share, rocky01 mounts it on /mnt/share). Nothing is deleted; you stop and start the NFS service only.

On ubu01, stop the NFS server, then on rocky01 touch the mount:

# on ubu01:
sudo systemctl stop nfs-server
# on rocky01:
ls /mnt/share &
sleep 20
uptime
ps -eo stat,pid,user,wchan:24,cmd | awk '$1 ~ /^D/'

You should see something like this:

 11:30:18 up  2:16,  2 users,  load average: 2.90, 1.12, 0.63
D       3402 student  rpc_wait_bit_killable  ls --color=auto /mnt/share

The ls hangs, because the mount is a hard NFS mount (the default), so the kernel waits for the server forever instead of returning an error. The task sits in D state blocked in an RPC call, and the load average climbs even though rocky01’s own CPU and local disk are idle. Look at where it’s stuck:

P=$(pgrep -f '/mnt/share'); sudo cat /proc/$P/stack

You should see something like this:

[<0>] rpc_wait_bit_killable+0x18/0x90 [sunrpc]
[<0>] __rpc_execute+0x114/0x3d0 [sunrpc]
[<0>] rpc_run_task+0x136/0x180 [sunrpc]
[<0>] nfs4_call_sync_sequence+0x60/0xb0 [nfsv4]

The rpc_wait_bit_killable and nfsv4 frames say plainly: this is waiting on NFS, not local disk. At work this is why one dead NFS server can make a whole fleet of clients look “slow” with high load. Bring it back:

# on ubu01:
sudo systemctl start nfs-server

The ls completes on its own within a few seconds and the load falls. If the server were gone for good, you’d force the mount down with sudo umount -f -l /mnt/share; the lazy -l detaches it so new access stops hanging, and the hard -f tries to kill the pending calls.

✅ Checkpoint: The ls hung in D state with an NFS stack while the server was down, and completed once you restarted nfs-server, leaving /mnt/share working.

7Recognise steal time on a VM

You can’t force steal in a lab, but you must recognise it. On a busy hypervisor, vmstat looks like this:

procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st gu
 3  0      0  998220   4908 381020    0    0     0     0 1120  980 41  3 20  0 36  0
 3  0      0  998220   4908 381020    0    0     0     0 1098  965 39  4 18  0 39  0

Here r is 3 on 2 CPUs and us is only about 40, yet id is 0, because st is 36 to 39: more than a third of the time the VM wanted a CPU and the host gave the physical core to another guest. The fix isn’t in your VM at all, it’s the overloaded host, so this is a conversation with the virtualisation or cloud team. On your own rocky01, st stays 0 because the laptop isn’t overcommitted.

✅ Checkpoint: You can point at the st column and explain that high steal means the hypervisor, not your VM, is the limit.

8Write the one-line verdict

Put it together. For any slow-server ticket, these four commands give you the answer:

echo "CPUs: $(nproc), load: $(cut -d' ' -f1-3 /proc/loadavg)"
vmstat 1 3 | tail -n 2
ps -eo stat,pid,cmd | awk '$1 ~ /^D/' | head
iostat -xz 1 2 | tail -n 4

Read them in order and you can say, in one sentence, which it is: “load 4 on 2 CPUs, us 99, no D tasks, disks idle: CPU-bound, process X”, or “load 4, us 1, wa 50, three D tasks on sda with 9 ms await: disk-bound, process Y”. That sentence, backed by the numbers, is what stops a team ordering CPUs for a disk problem.

✅ Checkpoint: You produced a one-line CPU-versus-I/O verdict for rocky01 from nproc, vmstat, the D-state list and iostat.

Break It, Then Fix It

Take a snapshot of rocky01 first (VBoxManage snapshot rocky01 take before-l73-break). The first trap is the whole point of the lesson: high load that is not a CPU shortage. The second is a tempting “fix” that makes things worse.

1Mistake high load for a CPU shortage

Create the kind of load a stuck disk or a heavy backup gives you, then look only at the load number:

cd ~/lab73
stress-ng --hdd 4 --hdd-opts dsync --hdd-bytes 256M --temp-path ~/lab73 --timeout 240s &
sleep 90
uptime
top -b -n 1 | head -n 3

You should see something like this:

[1] 3120
 12:03:12 up  2:49,  2 users,  load average: 3.92, 1.86, 0.79
top - 12:03:12 up  2:49,  2 users,  load average: 3.92, 1.86, 0.79
Tasks: 146 total,   0 running, 146 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.2 us,  6.9 sy,  0.0 ni, 21.5 id, 70.4 wa,  0.0 hi,  0.0 si,  0.0 st

A load of 3.92 on 2 CPUs reads like “we need more CPUs”, and that is the request that lands on the virtualisation team on a bad day. But look closer: 0 tasks running, us 1.2, and 70% iowait. Doubling the vCPUs would change nothing, because not one of these tasks is waiting for a CPU.

Fix: confirm it is I/O, find the D-state tasks and the device, then deal with the source, not the CPU count.

vmstat 1 3 | tail -n 2
ps -eo stat,pid,user,cmd | awk '$1 ~ /^D/'
iostat -xz 1 2 | tail -n 3

You should see something like this:

procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st gu
 0  4      0 1009120   4908 388612    0    0     0  3620 2390 3402  1  7 21 71  0  0
D+      3122 student  stress-ng-hdd [run]
D+      3123 student  stress-ng-hdd [run]
D+      3125 student  stress-ng-hdd [run]
sda  0.00 0.00 0.00 0.00 0.00 0.00 905.00 3620.00 0.00 0.00 8.77 4.00 880.00 3.95 11.42 99.80

b is 4, the writers are in D state, and sda has 11 requests queued with w_await near 9 ms. The load is made of tasks waiting for storage. At work the real fix is finding what writes (a backup that ran into business hours, a log compressor, a bad SAN path) and slowing or moving it.

Stop the test with kill %1 or wait for the timeout.

✅ Checkpoint: You showed the load was iowait, not CPU, with vmstat b/wa, the D-state list and iostat await.

2Try to “fix” a D-state process by killing it

Start the disk load again, find a stuck writer, and try to kill it:

stress-ng --hdd 2 --hdd-opts dsync --hdd-bytes 256M --temp-path ~/lab73 --timeout 120s &
sleep 10
P=$(ps -eo stat,pid,cmd | awk '$1 ~ /^D/ {print $2; exit}')
echo "trying to kill $P"
sudo kill -9 "$P"
sleep 2
ps -o stat,pid,cmd -p "$P"

You should see something like this:

trying to kill 3205
    STAT   PID CMD
    D      3205 stress-ng-hdd [run]

kill -9 did nothing: the process is still there, still in D state. A task in uninterruptible sleep can’t take a signal until the kernel call it’s stuck in returns, so you cannot kill your way out of an I/O stall or an NFS hang. People retry kill -9 again and again and conclude the server is “broken”.

Fix: deal with the cause, not the process. For disk load, let the I/O drain or stop the source; the task leaves D on its own. Here, stop the whole test:

kill %1
sleep 3
ps -eo stat,pid,cmd | awk '$1 ~ /^D/' | wc -l

You should see:

0

Once the writes finish, the workers return to normal and exit. For a real NFS hang the equivalent is recovering the server or umount -f -l; for a bad disk it’s the storage fix. The lesson is that D state is a symptom of what the task waits on, not a process you can force away.

✅ Checkpoint: kill -9 left the D-state task in place, and it only cleared once the I/O source was stopped.

This happened at work

The load alert that wasn’t a CPU problem

At a semiconductor design house in Singapore, the overnight chip-simulation jobs ran on a shared Linux server, and one week the monitoring kept paging with “load average 40” around 3 a.m. The on-call engineer’s first instinct, and the design team’s loud request, was to add CPUs: the box had 16 cores and a load of 40 surely meant it was starved.

Before filing for more hardware, the engineer caught it live one night. vmstat 1 told a different story: r was 2, us and sy together were under 10, but b was often above 30 and wa sat near 90. The CPUs were almost idle. ps -eo stat,pid,cmd filtered on D showed dozens of simulation processes all stuck, and /proc/PID/stack on a few of them pointed at NFS calls.

The jobs read and wrote their scratch data on an NFS share from a filer that was itself overloaded between 2 and 4 a.m., when backups and another team’s jobs hit it. Every simulation that touched the share went into D state waiting for the filer, and all those waiting tasks drove the load average to 40 while the 16 cores did nothing.

Adding CPUs would have wasted money and fixed nothing. The team moved the scratch I/O to local NVMe, staggered the jobs away from the backup window, and raised the filer’s limits with the storage team. The runbook gained one rule the whole team now follows: on a high-load page, run vmstat first and look at wa and b before anyone says the word CPU.

Common mistakes and troubleshooting

MistakeWhat happensWhat to do instead
Reading load average without the CPU countA load of 8 scares people on a 32-core box, 2 is ignored on a 2-vCPU VMDivide by nproc and compare with the server’s normal
Assuming high load means a CPU shortageYou order CPUs for a disk or NFS problemCheck wa and the b column in vmstat 1, and list D-state tasks
Trusting the first line of vmstat or iostatIt’s the average since boot, so a live problem looks smallUse an interval and read from the second report on
Trying to kill -9 a D-state processNothing happens; it can’t take a signal while blockedFix what it waits on: the disk, or recover/force-unmount NFS
Treating %util 100 as “disk full” on SSD, NVMe or SANYou chase a device that still has headroomRead r_await, w_await and aqu-sz before %util
Ignoring st (steal) on a VMYou tune an app that the hypervisor is starvingWatch st in vmstat or %steal in sar; escalate to the host team
Blaming the client when NFS makes load spikeYou reboot healthy serversCheck /proc/PID/stack for NFS/rpc frames; fix the server or umount -f -l

Interview questions

Click a question to open it. Each one is written the way an interviewer would ask it. Under it you will see why they ask it and one way you could answer, in your own words.

Fresher level

1. A server shows a load average of 8. Is that bad?

Why they ask this: They want to hear that load means nothing without the CPU count and the server’s normal.

How you might answer: It depends on the CPU count. I’d run nproc: 8 on a 2-vCPU VM means tasks are waiting a lot, but on a 16-core server 8 is comfortable.

Then I’d check whether it’s CPU or I/O, because load also counts tasks waiting on disk. vmstat 1 tells me: high r and us is CPU, high b and wa is storage, and I compare against what’s normal for that box.

2. What is a D-state process, and why does it matter for load?

Why they ask this: A basic but revealing question; they want to know you understand uninterruptible sleep.

How you might answer: D state is uninterruptible sleep: the task is blocked in a kernel call, almost always waiting for disk or NFS, and it can’t take a signal until that call returns, so kill -9 won’t touch it.

It matters because Linux counts D-state tasks in the load average alongside running ones. So a stuck disk or a dead NFS mount can push load very high while the CPUs are idle, which is why I always check wa and the b column, not just the load number.

2–5 years experience

3. Load is 12 on 4 CPUs, but top shows the CPUs 70% idle. Walk me through it.

Why they ask this: They’re testing whether you move from symptom to device to process instead of guessing.

How you might answer: Idle CPUs with high load means tasks blocked on I/O. I’d confirm with vmstat 1 (b column and wa), list D-state tasks with ps -eo stat,pid,cmd | awk '$1 ~ /D/', and run iostat -xz 1 to find which device has high await and a long queue.

Then pidstat -d 1 or iotop names who is doing the I/O. If no local process is busy but await is still high, I’d look at /proc/PID/stack for NFS or SAN frames, and check dmesg and multipath. The fix follows the evidence, not the load number.

4. An NFS server went down and now every client has a load of 30 and feels frozen. What’s happening and what do you do?

Why they ask this: They want to see you connect NFS hangs, D state, and the recovery options.

How you might answer: The clients mount it hard by default, so any process that touches the share goes into D state waiting for the server forever, and all those waiting tasks drive the load average up even though the clients’ own CPUs are idle. The processes can’t be killed while they wait.

First I’d recover the NFS server, which lets the hung calls complete and the load fall on its own. If it’s gone for good, I’d umount -f -l the mount on the clients so new access stops hanging, and longer term consider soft mounts with sensible timeo and retrans values, or a health check, weighing the data-safety trade-off of soft mounts.

Senior level

5. On a VM, load is high and users complain, but your app and disk look fine. What do you suspect and how do you confirm?

Why they ask this: Seniors should reach for steal time and know it’s a host problem.

How you might answer: I’d suspect CPU steal: the hypervisor giving our vCPUs to other guests. I’d confirm with the st column in vmstat 1 or %steal in mpstat and sar. Steal above a few percent, sustained, means we’re being starved by the host.

If sar has history (Lesson 53), I’d pull %steal over the slow window to show a pattern, for example every morning at 9. That evidence goes to the virtualisation or cloud team, because the fix is on the host, moving the VM or reducing overcommit, not anything I can tune inside the guest.

6. How would you design alerting so a high-load page is actually actionable across a large fleet?

Why they ask this: They want layered metrics and avoiding the “load alone” trap, not just a threshold.

How you might answer: I wouldn’t alert on raw load. I’d alert on load per CPU combined with what’s behind it: sustained high us with a run queue over the CPU count for CPU pressure, sustained high wa with a non-zero b and disk await for I/O pressure, and %steal over a threshold as its own alert.

Each alert would link to a runbook that starts with vmstat, iostat and the D-state list, so whoever is on call collects the same evidence and reaches the same CPU-versus-I/O verdict. I’d keep PSI (/proc/pressure) in mind too, since it measures stall time directly and is cleaner than load for this.

Quick quiz

Q1. rocky01 has 2 vCPUs and a 1-minute load of 2.00 with us at 100 and wa at 0. What is it?

  1. Idle
  2. Disk-bound
  3. CPU fully used, nobody waiting on average
  4. Out of memory
Show answer

C — Load equal to the CPU count with high us and no iowait means the CPUs are fully used with no queue on average.

Q2. Which task state, counted in load average, can make load high while CPUs stay idle?

  1. Zombie (Z)
  2. Stopped (T)
  3. Uninterruptible sleep (D), waiting on I/O
  4. Sleeping (S)
Show answer

C — Linux counts R and D tasks in load; D tasks blocked on disk or NFS raise load without using CPU.

Q3. In vmstat 1, which pair points to a disk bottleneck?

  1. r and us
  2. si and so
  3. b and wa
  4. in and cs
Show answer

C — b is tasks blocked on I/O and wa is CPU time spent idle waiting for I/O.

Q4. Why does kill -9 fail to end a process stuck in D state?

  1. It needs sudo
  2. The process ignores SIGKILL
  3. It can’t take a signal until the kernel call it’s blocked in returns
  4. It’s a zombie
Show answer

C — Uninterruptible sleep means no signals are delivered until the blocking I/O (disk or NFS) completes.

Q5. On a VM, vmstat shows r 3, us 40, id 0 and st 38. What does that tell you?

  1. The disk is full
  2. The hypervisor is giving your vCPUs to other guests
  3. Memory is leaking
  4. The app needs tuning
Show answer

B — High steal (st) means your vCPU was ready but the host ran another guest; the fix is on the host.

Quick recap

  • Load average counts running, queued and D-state (I/O-waiting) tasks; always divide by nproc and compare with normal.
  • vmstat 1 splits it: r and us high is CPU-bound, b and wa high is I/O-bound, st high is the hypervisor.
  • iostat -xz 1 finds the slow device: read r_await, w_await and aqu-sz before %util.
  • List D-state tasks with ps -eo stat,pid,cmd | awk '$1 ~ /D/' and read /proc/PID/stack to see disk vs NFS.
  • A D-state task can’t be killed, not even with kill -9; fix what it waits on, or umount -f -l a dead NFS mount.
  • High load is a doorbell: vmstat, iostat and pidstat tell you whether to touch the CPU, the disk or the host.

Next lesson

Lesson 74: Out of memory and the OOM killer → You’ll see how the kernel picks a process to kill when memory runs out, read the OOM messages in dmesg and journalctl -k, and tune oom_score_adj and swap.

Further reading: Linux high load average

Want to go deeper into Linux high load average? Read the man7.org pages for proc_loadavg(5), vmstat(8) and iostat(1), the kernel’s Pressure Stall Information page, and the stress-ng project. You can also see the full plan for this course on the Linux Administration course page.

Scroll to Top

This website uses cookies

We use a few cookies to keep this site working. If you allow it, we also use Google Analytics to count visits so we can improve our lessons. You can accept all, deny the optional ones, or choose what to allow.