"It is slow" needs to become "it is waiting on X" before you can fix anything. There are three candidates. This lab creates each bottleneck deliberately so you can see what each one looks like.
1. Establish a baseline
nproc
uptime
free -h
vmstat 1 3Read load against nproc, always. A load of 8 on 16 cores is half-busy; on 2 cores it is a queue four deep. In vmstat, the columns that matter are r (runnable, waiting for CPU), b (blocked on I/O), si/so (swap in/out), and wa (CPU time spent waiting for I/O).
Verify
2. Use pressure stall information first
cat /proc/pressure/cpu
cat /proc/pressure/memory
cat /proc/pressure/ioPSI is the fastest triage on any modern kernel and it is criminally underused. Each line gives the percentage of time _something_ was stalled waiting on that resource, averaged over 10s, 60s and 300s. some means at least one task stalled; full means everything did.
Read all three, take the largest avg10, and you have your bottleneck in one screen — no interpretation of load averages required.
Verify
3. Create CPU starvation and recognise it
stress-ng --cpu "$(nproc)" --timeout 30s &
sleep 10
uptime
cat /proc/pressure/cpu
vmstat 1 3
top -bn1 | head -n 12
waitThe signature: load climbs toward and past nproc, /proc/pressure/cpu avg10 rises, vmstat's r column exceeds the core count, and wa stays near zero. Nothing is waiting on disk — there simply is not enough CPU.
Verify
4. Create memory pressure and recognise it
free -h
stress-ng --vm 2 --vm-bytes 40% --timeout 30s &
sleep 10
free -h
cat /proc/pressure/memory
vmstat 1 3
waitA different signature: free shows available collapsing, /proc/pressure/memory rises, and if swap exists si/so become non-zero. Sustained swap traffic is the worst of the three — everything becomes disk-slow at once.
Read the available column, not free. Linux uses spare memory as page cache by design, so low free with high available is a healthy machine, and "we are out of memory" read off the wrong column is the most common false alarm in this job.
Verify
5. Create I/O pressure and recognise it
iostat -xz 1 3
stress-ng --hdd 2 --hdd-bytes 512M --timeout 30s &
sleep 10
iostat -xz 1 3
cat /proc/pressure/io
vmstat 1 3
waitThe columns worth reading in iostat -xz: %util (how busy the device is), aqu-sz (average queue depth), and r_await/w_await (milliseconds a request waited). High await with high %util is a saturated device. High await with _low_ %util on cloud storage usually means you have exhausted a provisioned IOPS or throughput allowance — the device is idle because the allowance is spent.
The matching signature elsewhere: vmstat's wa climbs and b is non-zero, while r stays low. Plenty of CPU, all of it waiting.
Verify
6. Find the process responsible
sudo pidstat -u -r -d 1 3 2>/dev/null | tail -n 20
ps -eo pid,ppid,pcpu,pmem,rss,etime,cmd --sort=-pcpu | head -n 8
ps -eo pid,pcpu,pmem,rss,cmd --sort=-rss | head -n 8
sudo iotop -b -n 2 2>/dev/null | head -n 12 || echo "iotop not installed"pidstat -u -r -d gives CPU, memory and disk per process in one table, which is the fastest way from "the machine is I/O bound" to "this PID is doing it".
Verify
The five-minute routine
cat /proc/pressure/{cpu,memory,io}— which resource, in one screenuptimeagainstnproc— how deep the queue isfree -h, readingavailable— memory headroomiostat -xz 1 3—%util,aqu-sz,awaitfor storagepidstat -u -r -d 1 3— which processdmesg -T | tail— OOM kills and device errors leave evidence here
sudo dmesg -T | grep -i -E "oom|killed process|error" | tail -n 5Where this goes next
Six guided labs. The last one is a challenge: a machine with a real problem and no indication of which of the three it is.