When a server feels slow, the first question is which resource is saturated and which process is responsible. Four classic terminal tools answer it: top and htop for CPU and memory, iotop for disk I/O and iftop for network bandwidth. In this tutorial you will install them on Ubuntu 24.04, learn to read the numbers that matter and use them to track down a real bottleneck.

Prerequisites

To follow this guide you need:

  • A server running Ubuntu 24.04 LTS, for example a CubePath VPS. The tools work the same on Debian 12; on Rocky Linux 9, install htop and iftop from EPEL.
  • A non-root user with sudo privileges. iotop and iftop need root to read kernel counters and capture packets.

Step 1 - Installing the tools

top is part of the procps package and is always installed. Install the other three:

sudo apt update
sudo apt install htop iotop iftop

Verify that they are available:

htop --version
iftop -h 2>&1 | head -n 1
command -v iotop
htop 3.3.0
iftop: display bandwidth usage on an interface by host
/usr/sbin/iotop

Step 2 - Reading the system summary in top

Start top:

top

The first five lines are the most useful part of the screen:

top - 10:42:17 up 12 days,  3:05,  1 user,  load average: 2.14, 1.08, 0.61
Tasks: 142 total,   2 running, 140 sleeping,   0 stopped,   0 zombie
%Cpu(s): 46.3 us,  4.1 sy,  0.0 ni, 37.2 id, 11.8 wa,  0.0 hi,  0.3 si,  0.3 st
MiB Mem :   3915.2 total,    214.6 free,   2140.8 used,   1829.4 buff/cache
MiB Swap:   2048.0 total,   1998.0 free,     50.0 used.   1774.4 avail Mem

How to read them:

  • load average: the average number of processes running or waiting for CPU or disk over 1, 5 and 15 minutes. Compare it with the number of vCPUs (nproc). On a 2 vCPU server, a sustained load above 2 means work is queuing. Here the 1-minute value is rising above the 15-minute one, so the load is increasing.
  • us / sy: CPU time spent in user programs and in the kernel. High us points to your application; high sy often to heavy I/O or many system calls.
  • wa (I/O wait): the CPU is idle waiting for the disk. A consistently high value, like the 11.8 above, means the disk is the bottleneck, not the CPU. Use iotop (Step 5) to find out who is reading or writing.
  • st (steal): time the hypervisor gave your vCPU to another virtual machine. On a VPS, a steal value that stays above a few percent means you are short of CPU capacity.
  • avail Mem: memory available for new work without swapping. Ignore free: Linux uses spare RAM as disk cache (buff/cache) and gives it back when needed. Worry when avail Mem is low and swap used keeps growing.

Below the summary is the process list. The columns to watch are %CPU, %MEM, RES (physical memory used by the process) and S (state: R running, S sleeping, D uninterruptible sleep, usually waiting for disk, Z zombie).

Step 3 - Using top interactively and in scripts

While top is running, these keys change what you see:

KeyAction
PSort by CPU usage (default)
MSort by memory usage
1Show each CPU core separately
cShow the full command line instead of the program name
uShow only processes of one user
kSend a signal to a PID (default SIGTERM)
qQuit

You can also set the sort order and filters from the command line. To watch only the processes of the www-data user sorted by memory:

top -u www-data -o %MEM

To follow specific PIDs, for example all PostgreSQL processes:

top -p "$(pgrep -d, postgres)"

Batch mode (-b) prints plain text instead of a live screen, which is useful to capture a snapshot when an alert fires or to attach to a ticket. This prints one iteration and keeps the summary plus the 10 heaviest processes:

top -b -n 1 -o %CPU | head -n 17

Step 4 - Working faster with htop

htop shows the same data with per-core CPU bars, color-coded memory usage, mouse support and a process tree. Start it:

htop

The CPU bars are split by color: green for user processes, red for the kernel, and, once enabled in the setup screen, gray for I/O wait and other colors for steal. The memory bar distinguishes used memory (green) from buffers and cache.

The function keys are listed at the bottom of the screen:

KeyAction
F3 or /Search for a process by name
F4 or \Filter the list to matching processes only
F5 or tToggle tree view to see parent and child processes
F6 or < >Choose the sort column
SpaceTag a process (tag several to act on all of them)
F9 or kSend a signal to the selected or tagged processes
F2Setup: add meters, columns and color options
HHide or show user threads

Tree view is particularly useful on servers: it shows, for example, that 40 php-fpm processes all belong to one pool master, or which parent keeps spawning short-lived children.

htop also accepts start-up options. To show only one user's processes in tree view:

htop -u www-data -t

Settings you change with F2 are saved in ~/.config/htop/htoprc for your user.

Step 5 - Finding disk I/O hogs with iotop

top tells you the disk is the bottleneck (high wa), but not who is using it. iotop shows read and write bandwidth per process.

Since Linux 5.14, the kernel does not collect per-task delay accounting by default, so the IO> and SWAPIN percentages in iotop stay empty. Enable it for the current boot:

sudo sysctl kernel.task_delayacct=1

To keep it after a reboot, add it to a sysctl drop-in file:

echo 'kernel.task_delayacct = 1' | sudo tee /etc/sysctl.d/60-iotop.conf

Now run iotop, showing only processes that are actually doing I/O (-o):

sudo iotop -o
Total DISK READ:        12.34 M/s | Total DISK WRITE:        48.71 M/s
Current DISK READ:      12.30 M/s | Current DISK WRITE:      52.90 M/s
    TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND
   2231 be/4 mysql      10.96 M/s   41.02 M/s  0.00 %  78.41 % mysqld
   5120 be/4 root        1.34 M/s    7.61 M/s  0.00 %  12.03 % rsync -a /var/www/ /backup/

The IO> column is the share of time the thread spent waiting for I/O. In this example MySQL causes most of the load, and a backup rsync competes with it.

Other useful options:

  • sudo iotop -o -P shows processes instead of individual threads.
  • sudo iotop -o -a shows accumulated I/O since iotop started instead of current bandwidth, which reveals processes that write in short bursts.
  • sudo iotop -o -b -n 5 -d 2 prints five samples, two seconds apart, as plain text for logs or tickets.

If a backup job is the problem, you can lower its I/O priority instead of stopping it:

sudo ionice -c 3 -p 5120

Class 3 (idle) means the process only gets disk time when nobody else needs it.

Step 6 - Measuring network bandwidth with iftop

iftop shows which connections are using bandwidth on an interface, similar to top for network traffic. First find your public interface name:

ip -br addr
lo               UNKNOWN        127.0.0.1/8 ::1/128
eth0             UP             203.0.113.10/24 2001:db8::10/64

Run iftop on that interface. -n disables DNS lookups (which would add traffic and slow the display), -P shows ports and -B shows bytes instead of bits:

sudo iftop -i eth0 -n -P -B
203.0.113.10:443      => 198.51.100.24:51522     1.21MB  1.05MB  987KB
                      <=                          22.4KB  19.8KB  18.1KB
203.0.113.10:22       => 192.0.2.55:60114         3.10KB  2.85KB  2.64KB
                      <=                           416B    390B    402B
----------------------------------------------------------------------
TX:      cum:  48.2MB   peak:  1.34MB   rates:  1.21MB  1.07MB   991KB
RX:             1.10MB          31.0KB           23.2KB  20.6KB  19.1KB
TOTAL:          49.3MB          1.37MB           1.23MB  1.09MB  1.01MB

Each pair of lines is one connection: => is traffic sent, <= is traffic received. The three columns are average rates over the last 2, 10 and 40 seconds. Here most outgoing traffic is HTTPS to a single client, typical of a large download.

Keys while iftop runs: p toggles ports, n toggles DNS resolution, t cycles the line display, 1/2/3 sort by the 2, 10 or 40 second column, and q quits.

To focus on specific traffic, pass a packet filter with -f. For example, only web traffic:

sudo iftop -i eth0 -n -P -f 'port 80 or port 443'

Step 7 - Putting it together: a troubleshooting workflow

When an alert fires or users report slowness, work through the resources in order:

  1. Run top and look at the load average against nproc, then at us, wa and st.
  2. If us or sy is high, sort by CPU (P) in top or htop and look at the heaviest processes. Use tree view in htop to see which service they belong to.
  3. If wa is high or processes sit in state D, run sudo iotop -o -P to find the process reading or writing.
  4. If avail Mem is low and swap usage grows, sort by memory (M). Check sudo journalctl -k | grep -i 'out of memory' to see if the kernel has already killed processes.
  5. If CPU, disk and memory look normal but the service is slow or the link is saturated, run sudo iftop -i eth0 -n -P to see who is using the bandwidth.

These tools only show the present moment. For history and alerting, you need a metrics system that records the same data over time.

Troubleshooting

  • iotop shows CONFIG_TASK_DELAY_ACCT not enabled in kernel, cannot determine SWAPIN and IO %: delay accounting is off. Enable it with sudo sysctl kernel.task_delayacct=1 as shown in Step 5.
  • iftop: pcap_open_live(eth0): eth0: No such device exists: the interface has a different name, such as ens3 or enp1s0. List interfaces with ip -br addr and pass the right one with -i.
  • iftop shows no traffic or Permission denied: it needs root to capture packets. Run it with sudo.
  • htop colors look wrong or the screen is garbled over SSH: your terminal type is not recognized. Try TERM=xterm-256color htop.

Conclusion

You can now use top and htop to see CPU and memory pressure, iotop to find the processes behind disk I/O wait and iftop to see which connections use your bandwidth, and you know which numbers in each tool point to a real bottleneck. As next steps, learn to read system logs with journalctl to see why a process misbehaved, and set up a metrics and alerting stack so you are notified before a resource runs out.