ECK
Back to Blog
4 min read

Linux System Optimization: A Performance Tuning Guide

Practical techniques for optimizing Linux systems — from kernel parameters and I/O schedulers to memory management and process tuning.

linuxperformancesystem-administrationoptimization

Linux System Optimization: A Performance Tuning Guide

Linux gives you granular control over system performance, but knowing where to look and what to tune requires experience. This guide covers the techniques I use regularly on both servers and my daily-driver Arch Linux workstation.

Rule Zero: Measure First

Never optimize blindly. Profile, identify the bottleneck, then optimize. The tools:

# System overview
htop           # Interactive process viewer
btop           # Modern alternative to htop
vmstat 1       # Virtual memory statistics (1-second interval)
iostat -x 1    # I/O statistics

# Detailed analysis
perf top       # Real-time CPU profiling
perf record -g ./my_program && perf report  # Profile a specific program
strace -c ./program   # System call summary

Kernel Parameters (sysctl)

Memory Management

# Reduce swappiness (default: 60)
# Lower = prefer keeping processes in RAM
# For workstations with plenty of RAM:
vm.swappiness = 10

# For servers running databases:
vm.swappiness = 1

# VFS cache pressure (default: 100)
# Lower = keep directory/inode caches longer
vm.vfs_cache_pressure = 50

# Dirty page writeback — when to start writing dirty pages to disk
vm.dirty_ratio = 15          # % of RAM before sync write
vm.dirty_background_ratio = 5 # % of RAM before async write

Network Tuning

# Increase network buffer sizes
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# Enable TCP BBR congestion control
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# Connection handling
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

Apply with:

# Temporary (until reboot)
sudo sysctl -w vm.swappiness=10

# Persistent — add to /etc/sysctl.d/99-custom.conf
sudo sysctl --system  # Reload all

I/O Schedulers

Modern NVMe drives work best with specific schedulers:

# Check current scheduler
cat /sys/block/nvme0n1/queue/scheduler

# For NVMe SSDs: 'none' or 'mq-deadline'
echo "none" | sudo tee /sys/block/nvme0n1/queue/scheduler

# For SATA SSDs: 'mq-deadline'
echo "mq-deadline" | sudo tee /sys/block/sda/queue/scheduler

# For HDDs: 'bfq' (Budget Fair Queueing)
echo "bfq" | sudo tee /sys/block/sdb/queue/scheduler

Make persistent with a udev rule (/etc/udev/rules.d/60-iosched.rules):

# NVMe
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
# SATA SSD
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"
# HDD
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"

CPU Performance

Governor and Frequency Scaling

# Check current governor
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

# For maximum performance (servers, benchmarking)
echo "performance" | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

# For desktop use — schedutil is ideal (frequency scales with load)
echo "schedutil" | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

Process Priority and CPU Affinity

# Run CPU-intensive task with high priority
nice -n -10 ./heavy_computation

# Pin process to specific cores (useful for NUMA or isolating workloads)
taskset -c 0-3 ./my_process

# Real-time priority (use with caution)
chrt -f 50 ./realtime_process

Memory Optimization

Transparent Huge Pages

For most workloads, THP helps. For databases (Redis, MongoDB), it can hurt:

# Check current state
cat /sys/kernel/mm/transparent_hugepage/enabled

# Disable for databases
echo "never" | sudo tee /sys/kernel/mm/transparent_hugepage/enabled

zswap/zram

Compress memory before swapping to disk — especially useful on machines with limited RAM:

# Enable zswap (compresses swap in RAM)
echo 1 | sudo tee /sys/module/zswap/parameters/enabled
echo lz4 | sudo tee /sys/module/zswap/parameters/compressor
echo 20 | sudo tee /sys/module/zswap/parameters/max_pool_percent

# Or use zram (compressed RAM block device as swap)
sudo modprobe zram
echo lz4 | sudo tee /sys/block/zram0/comp_algorithm
echo 4G | sudo tee /sys/block/zram0/disksize
sudo mkswap /dev/zram0
sudo swapon -p 100 /dev/zram0

Filesystem Tuning

Mount Options

# /etc/fstab optimizations for ext4 on SSD
/dev/nvme0n1p2  /  ext4  noatime,commit=60  0  1

# For btrfs
/dev/nvme0n1p2  /  btrfs  noatime,compress=zstd:1,ssd,space_cache=v2  0  0

noatime avoids writing access timestamps on every read — a simple but effective optimization.

Monitoring and Alerts

Set up basic monitoring to catch issues before they become problems:

# Simple disk space alert (add to crontab)
#!/bin/bash
threshold=90
usage=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$usage" -gt "$threshold" ]; then
    echo "Disk usage critical: ${usage}%" | mail -s "Disk Alert" admin@example.com
fi

My Arch Linux Workstation Config

Key optimizations on my daily driver:

  1. Kernel: linux-zen (optimized for desktop responsiveness)
  2. Scheduler: schedutil governor, none I/O scheduler for NVMe
  3. Memory: swappiness=10, zram with lz4
  4. Filesystem: btrfs with zstd compression and noatime
  5. Network: TCP BBR congestion control

Conclusion

Linux performance tuning is about understanding your workload and matching the system configuration to it. There's no one-size-fits-all — a database server needs different tuning than a desktop workstation. Always measure before and after changes, and make one change at a time so you can attribute improvements correctly.