Skip to main content
RunBook Academy

LinuxXLII · Network PerformanceHistorical data

sar -n - network history and the counters nobody collected

Advanced⏱ ~18 minsarsadf

What you'll learn

  • Read a past incident window with sar -n against an archived data file
  • Verify that the activities you need were collected, and fix SADC_OPTIONS when they were not
  • Compute interface utilisation against link speed, and recognise when %ifutil is meaningless
  • Explain how a 10-minute collection interval hides a 30-second saturation event

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-11

Not yet marked complete on this device.

Live network tools answer “what is happening now”. Every incident review asks “what was happening at 02:14 last Thursday”, and by then the counters have rolled over and the connection is closed.

sysstat answers that question, if it was collecting the right things. The second half of that sentence is where most of this lesson lives.

Reading a past window

Data files live in /var/log/sysstat (/var/log/sa on RHEL), one per day, named saNN where NN is the day of the month:

ls /var/log/sysstat/
sa03  sa04  sa05  sa06  sa07  sa08  sa09

Point sar at the file for the day and bound the window:

S_TIME_FORMAT=ISO sar -n DEV --iface=ens18 -f /var/log/sysstat/sa03 \
  -s 12:00:00 -e 12:40:00
Linux 7.0.0-27-generic (orchgrid) 	2026-08-03 	_x86_64_	(4 CPU)

12:00:37        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
12:10:37        ens18     15.72     10.93      1.75     21.96      0.00      0.00      0.00      0.00
12:20:19        ens18     20.57     13.52      2.34     34.44      0.00      0.00      0.00      0.00
12:30:00        ens18     28.45     18.92      2.96     58.05      0.00      0.00      0.00      0.00
Average:        ens18     21.52     14.42      2.34     37.97      0.00      0.00      0.00      0.00

S_TIME_FORMAT=ISO is worth making a habit. Without it sar uses the locale time format, which on many systems means a 12-hour clock with a separate AM/PM field - so every column position shifts by one and any awk you write against the output silently reads the wrong field.

--iface= is not optional in practice. A host running containers has a veth per container plus bridges, and without the filter sar -n DEV prints dozens of interfaces per interval and the one you care about scrolls away:

ls /sys/class/net/
br-034ca77f5100  docker0  ens18  lo  veth429b399  veth8bac050  vethb7894d8

Which keyword answers which question

sar -n DEV     # throughput and packet rates per interface
sar -n EDEV    # errors and drops per interface
sar -n SOCK    # sockets in use, including TIME_WAIT
sar -n TCP     # connection rate and segment rate
sar -n ETCP    # retransmits, resets, checksum errors
sar -n UDP     # datagrams and receive errors

EDEV is the one to reach for when something was dropping:

sar -n EDEV --iface=ens18 -f /var/log/sysstat/sa03

Its columns are rxerr/s, txerr/s, coll/s, rxdrop/s, txdrop/s, txcarr/s, rxfram/s, rxfifo/s, txfifo/s. rxdrop/s and txdrop/s are drops for lack of buffer space in the kernel; rxfifo/s is a NIC ring overrun. They point at different fixes, which is why the split matters.

ETCP carries retrseg/s, the retransmission rate. Combined with oseg/s from TCP it gives the retransmit ratio over a past window - the number the previous lesson showed dominates throughput on a long path:

sar -n TCP,ETCP -f /var/log/sysstat/sa03 -s 02:00:00 -e 02:30:00

%ifutil is computed from the interface speed the kernel reports. That works on a physical NIC and does not work on anything virtual:

for i in /sys/class/net/*; do
  printf '%-20s %s\n' "$(basename "$i")" "$(cat "$i/speed" 2>/dev/null || echo n/a)"
done
ens18                10000
docker0              -1
br-034ca77f5100      -1
lo                   n/a

-1 means the kernel has no speed for that interface, so %ifutil on it is meaningless - and it will report 0.00 rather than an error, which is the dangerous part. Only trust %ifutil on an interface whose speed file holds a real number.

For everything else, compute it. sar reports kB/s; the link speed is in Mbit/s:

S_TIME_FORMAT=ISO sar -n DEV --iface=ens18 -f /var/log/sysstat/sa03 \
  -s 12:00:00 -e 12:40:00 |
awk -v speed_mbit=10000 '
  $2 == "ens18" && $1 != "Average:" {
    rx_mbit = $5 * 8 / 1000
    tx_mbit = $6 * 8 / 1000
    printf "%s  rx %7.2f Mbit/s (%5.2f%%)  tx %7.2f Mbit/s (%5.2f%%)\n", \
           $1, rx_mbit, 100 * rx_mbit / speed_mbit, \
               tx_mbit, 100 * tx_mbit / speed_mbit
  }'
12:10:37  rx    0.01 Mbit/s ( 0.00%)  tx    0.18 Mbit/s ( 0.00%)
12:20:19  rx    0.02 Mbit/s ( 0.00%)  tx    0.28 Mbit/s ( 0.00%)
12:30:00  rx    0.02 Mbit/s ( 0.00%)  tx    0.46 Mbit/s ( 0.00%)

For a full-duplex link, utilisation is the larger of the two directions, not their sum - which is how sar defines %ifutil too.

The interval hides the event

systemctl cat sysstat-collect.timer | grep OnCalendar
OnCalendar=*:00/10

Every ten minutes. Each row of sar -n DEV is therefore an average over roughly 600 seconds, and a saturation event shorter than that is divided by 600 before you see it.

Concretely: a backup job saturates a 10 Gb link for 30 seconds. Averaged over ten minutes, that appears as 5% utilisation. The graph is flat. Meanwhile every request crossing that link during those 30 seconds timed out, and the incident report says “the network showed no anomaly”.

Three ways to deal with it:

Collect more often where it matters. A drop-in raises the rate without editing the vendor unit:

# /etc/systemd/system/sysstat-collect.timer.d/override.conf
[Timer]
OnCalendar=
OnCalendar=*:00/01

The empty OnCalendar= is required: it clears the vendor value before the new one is added, and omitting it leaves both active. One-minute collection multiplies the data volume by ten, so raise HISTORY deliberately or accept a shorter window.

Read EDEV rather than DEV. Errors and drops are counters, not rates - a 30-second burst still leaves its drops in the average, whereas throughput is diluted. rxdrop/s of 0.4 averaged over ten minutes means 240 packets were dropped in that window, which is a real finding even though the throughput graph is flat.

Keep a live capture running during known-risky windows. sar -n DEV 1 written to a file during a change window costs nothing and has the resolution the archive does not.

An incident walkthrough

Reports of timeouts between 02:10 and 02:20 on the 3rd:

DAY=/var/log/sysstat/sa03

# Was the link busy?
sar -n DEV --iface=ens18 -f "$DAY" -s 02:00:00 -e 02:30:00

# Was anything dropped?
sar -n EDEV --iface=ens18 -f "$DAY" -s 02:00:00 -e 02:30:00

# Were sockets exhausted or piling up in TIME_WAIT?
sar -n SOCK -f "$DAY" -s 02:00:00 -e 02:30:00

# Was the host itself busy, or waiting on I/O?
sar -u -f "$DAY" -s 02:00:00 -e 02:30:00
sar -q -f "$DAY" -s 02:00:00 -e 02:30:00

Run all five before forming a hypothesis. The commonest outcome is that the network was quiet and the run queue was not, and the “network timeout” was a host that could not schedule the thread to read the socket. Historical data is worth having precisely because it lets you rule things out - and a negative result from the archive is evidence, whereas a missing activity is not.

Knowledge check

Knowledge check · 5 questions

  1. Q1. sar -n TCP against an archived file prints "Requested activities not available in file". What does that mean?

  2. Q2. A 30-second saturation of a 10 Gb link can appear as 5% utilisation in sar data collected every 10 minutes.

  3. Q3. Why should you not trust the %ifutil column for docker0 or a bridge interface?

  4. Q4. You need one-minute network history on a host. Which steps are required? Select all that apply.

  5. Q5. Which sar keyword distinguishes packets dropped for lack of kernel buffer space from a NIC ring overrun?

Passing score: 75%. Answers are checked in this browser.