📈 sar & sysstat

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

The Time Machine for Performance Problems

The server was slow at 2:17 PM. By the time you get the call it is 3:45 PM and everything looks fine. Without historical data you are guessing. With sar (System Activity Reporter) you can go back in time and see exactly what the CPU, memory, disk, and network were doing at 2:17 PM — even days or weeks ago.

sysstat is the package that provides sar, iostat, mpstat, and pidstat. It runs a data collection daemon (sadc) every 10 minutes by default, storing performance snapshots that sar can replay on demand. Install it on every server you manage — you will need it eventually.

Examples

1
Install and Enable sysstat
# Install sysstat
sudo dnf install -y sysstat    # RHEL/Rocky
sudo apt install -y sysstat    # Debian/Ubuntu

# Enable data collection
sudo systemctl enable --now sysstat

# On Debian/Ubuntu -- also enable in the config file
sudo vi /etc/default/sysstat
# Set: ENABLED="true"
sudo systemctl restart sysstat

# Verify it is running and collecting
sudo systemctl status sysstat
ls -la /var/log/sa/

# Data files are stored as /var/log/sa/saDD (DD = day of month)
# sadc collects data every 10 minutes by default
ls -lh /var/log/sa/
ls /var/log/sa/ output:
-rw-r--r-- 1 root root 2.1M Apr 23 23:50 sa23 -rw-r--r-- 1 root root 2.1M Apr 24 23:50 sa24 -rw-r--r-- 1 root root 2.1M Apr 25 23:50 sa25 -rw-r--r-- 1 root root 876K Apr 26 10:30 sa26 -rw-r--r-- 1 root root 145K Apr 26 10:30 sar26
💡 Install sysstat on every server immediately. It uses negligible resources but is invaluable when problems occur. If it is not installed when you need it, you have no historical data. This is one of those tools where the cost of not having it is paid at the worst possible moment.
2
sar CPU Statistics
# Show today's CPU activity (all samples)
sar

# CPU activity for today in real time (2 second intervals)
sar 2 10

# CPU activity from yesterday's data file
sar -f /var/log/sa/sa25

# CPU activity for a specific time range today
sar -s 14:00:00 -e 15:00:00

# Per-CPU statistics (all cores)
sar -P ALL

# Show specific CPU core
sar -P 0    # CPU 0 only
sar output:
Linux 5.14.0 (bambustudio) 04/25/2026 _x86_64_ (4 CPU) 10:00:01 AM CPU %user %nice %system %iowait %steal %idle 10:10:01 AM all 3.42 0.00 1.18 0.15 0.00 95.25 10:20:01 AM all 4.11 0.00 1.44 0.22 0.00 94.23 10:30:01 AM all 18.44 0.00 4.22 12.18 0.00 65.16 <-- spike 10:40:01 AM all 3.88 0.00 1.21 0.18 0.00 94.73 02:10:01 PM all 45.22 0.00 8.44 22.15 0.00 24.19 <-- problem period 02:20:01 PM all 42.18 0.00 7.88 25.44 0.00 24.50 02:30:01 PM all 4.12 0.00 1.33 0.21 0.00 94.34
This is how you answer "it was slow at 2 PM." The 10-minute snapshots show exactly when CPU and iowait spiked. In the example above the 2:10-2:30 PM window clearly shows the problem — high CPU combined with 22-25% iowait. Now you know it was a combined CPU and disk I/O problem, and you know exactly when it started and ended.
3
sar Memory and Swap Statistics
# Memory statistics
sar -r

# Memory for a time range
sar -r -s 14:00:00 -e 15:00:00

# Swap statistics
sar -S

# Memory and swap from previous day
sar -r -f /var/log/sa/sa25
sar -S -f /var/log/sa/sa25

# Huge pages
sar -H
sar -r output (key columns):
10:00:01 AM kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit 10:10:01 AM 8234112 10987648 4521856 28.48 102400 3207168 6234112 39.24 02:10:01 PM 234112 456448 12521856 78.83 102400 3207168 14234112 89.52 <-- memory pressure 02:20:01 PM 124544 234112 12631424 79.52 102400 3207168 14534112 91.44 <-- worse 02:30:01 PM 8112640 10865152 4643328 29.23 102400 3207168 6534112 41.14
💡 Watch kbavail and %commit together. kbavail dropping to near zero means the system was running out of usable memory. %commit over 100% means the system has committed more virtual memory than physically exists — a sign of memory overcommit that leads to OOM kills.
4
sar Disk I/O Statistics
# Disk I/O statistics
sar -d

# With human-readable device names
sar -d -p

# Disk I/O for a time range
sar -d -p -s 14:00:00 -e 15:00:00

# Block device statistics from previous day
sar -d -p -f /var/log/sa/sa25
sar -d -p output:
10:00:01 AM DEV tps rkB/s wkB/s areq-sz aqu-sz await %util 10:10:01 AM sda 12.50 128.00 512.00 51.20 0.12 8.44 18.22 02:10:01 PM sda 245.88 1024.00 8192.00 37.48 4.22 32.18 98.44 <-- saturated 02:20:01 PM sda 238.12 896.00 7680.00 36.12 4.88 35.22 97.88 02:30:01 PM sda 13.22 132.00 524.00 49.88 0.14 8.88 19.44
98% disk utilization with 35ms await at 2:10 PM. Combined with the CPU/memory data from Example 2, the picture is clear: something triggered a massive disk write operation at 2:10 PM that saturated the disk, caused iowait to spike, and drove up memory usage. Now you have a specific time and symptoms to investigate in the application logs.
5
sar Network Statistics
# Network interface statistics
sar -n DEV

# Network errors
sar -n EDEV

# TCP statistics
sar -n TCP

# TCP errors
sar -n ETCP

# All network stats
sar -n ALL

# Network stats for a time range
sar -n DEV -s 14:00:00 -e 15:00:00
sar -n DEV output:
10:00:01 AM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil 10:10:01 AM ens3 245.22 312.44 48.22 125.44 0.00 0.00 0.00 0.12 02:10:01 PM ens3 4522.88 5244.12 8844.22 9122.44 0.00 0.00 0.00 14.88
sar -n EDEV is your network health check. Error packets, dropped packets, and collisions appear here. A steady stream of errors on a network interface indicates a hardware problem, duplex mismatch, or cable issue. These are easy to miss in real-time but stand out clearly in sar's historical data.
6
mpstat and pidstat
# mpstat -- per-CPU statistics in real time
mpstat 2 5               # all CPUs, 2 second intervals
mpstat -P ALL 2 5        # show each CPU individually
mpstat -P 0,1 2 5        # show CPU 0 and 1 only

# pidstat -- per-process CPU and I/O statistics
pidstat 2 5              # CPU stats for all active processes
pidstat -r 2 5           # memory stats
pidstat -d 2 5           # disk I/O stats per process
pidstat -u -r -d 2 5     # CPU + memory + disk combined

# pidstat for a specific process
pidstat -p 12345 2 5

# pidstat for processes matching a name
pidstat -C nginx 2 5
pidstat -d output (disk I/O per process):
10:22:15 AM UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command 10:22:17 AM 999 1234 0.00 85234.22 0.00 0 mysqld 10:22:17 AM 33 5678 0.00 38122.44 0.00 0 php-fpm7.4 10:22:17 AM 0 9012 45234.22 0.00 0.00 0 rsync
💡 pidstat -d names the process doing disk I/O. Like iotop but available historically via sar data files. When you know from sar that the disk was saturated at 2:10 PM, pidstat can tell you which process was responsible — if you were collecting pidstat data at the time.
7
Configure sysstat Collection Interval
# Default collection is every 10 minutes
# Check the cron/timer configuration
cat /etc/cron.d/sysstat           # RHEL
systemctl cat sysstat             # systemd timer

# RHEL cron config -- /etc/cron.d/sysstat
cat /etc/cron.d/sysstat
/etc/cron.d/sysstat:
# Run system activity accounting tool every 10 minutes */10 * * * * root /usr/lib64/sa/sa1 1 1 # Generate a daily summary of process accounting at 23:53 53 23 * * * root /usr/lib64/sa/sa2 -A
# Change to every 5 minutes for higher resolution
sudo vi /etc/cron.d/sysstat
# Change */10 to */5

# How long are data files kept?
grep HISTORY /etc/sysstat/sysstat 2>/dev/null || grep HISTORY /etc/sysconfig/sysstat

# Extend history to 60 days (default is 7 or 28)
sudo vi /etc/sysstat/sysstat
# Set: HISTORY=60

# Manually collect a data point right now
sudo /usr/lib64/sa/sa1 1 1    # RHEL path
sudo /usr/lib/sysstat/sa1 1 1  # Debian path
10 minutes is the default but 5 minutes is better. A performance spike that lasts 8 minutes can be missed entirely between 10-minute samples. For production servers, 5-minute collection gives much better resolution for incident investigation with minimal additional overhead.
8
Post-Incident Investigation Script
#!/bin/bash
# sar-investigate.sh -- pull sar data for a specific time window
# Usage: ./sar-investigate.sh 2026-04-25 14:00 15:00

DATE=$1      # YYYY-MM-DD
START=$2     # HH:MM
END=$3       # HH:MM

if [[ -z "$DATE" || -z "$START" || -z "$END" ]]; then
    echo "Usage: $0 YYYY-MM-DD HH:MM HH:MM"
    echo "Example: $0 2026-04-25 14:00 15:00"
    exit 1
fi

# Convert date to day-of-month for sa file
DAY=$(date -d "$DATE" +%d)
SAFILE="/var/log/sa/sa$DAY"

if [[ ! -f "$SAFILE" ]]; then
    echo "No sar data found for $DATE (looked for $SAFILE)"
    exit 1
fi

echo "========================================"
echo " SAR Investigation: $DATE $START - $END"
echo "========================================"

echo ""
echo "--- CPU ---"
sar -f "$SAFILE" -s "${START}:00" -e "${END}:00"

echo ""
echo "--- MEMORY ---"
sar -r -f "$SAFILE" -s "${START}:00" -e "${END}:00"

echo ""
echo "--- DISK I/O ---"
sar -d -p -f "$SAFILE" -s "${START}:00" -e "${END}:00"

echo ""
echo "--- NETWORK ---"
sar -n DEV -f "$SAFILE" -s "${START}:00" -e "${END}:00"

echo ""
echo "--- LOAD AVERAGE ---"
sar -q -f "$SAFILE" -s "${START}:00" -e "${END}:00"

echo "========================================"

Quick Reference

CommandWhat it shows
sarToday's CPU activity (all samples)
sar 2 10CPU activity live, 2-second intervals
sar -s 14:00 -e 15:00CPU activity for a time range today
sar -f /var/log/sa/sa25CPU from day 25's data file
sar -rMemory statistics
sar -SSwap statistics
sar -d -pDisk I/O with device names
sar -n DEVNetwork interface statistics
sar -n EDEVNetwork errors
sar -n TCPTCP statistics
sar -qLoad average and run queue
sar -P ALLPer-CPU statistics
mpstat -P ALL 2 5Real-time per-CPU statistics
pidstat -d 2 5Disk I/O per process
pidstat -C nginx 2 5Stats for processes named nginx

← Back to Performance Index ↑ Back to EXPANDED