Swap usage on a production web server is a symptom, not a cause — it means RAM is exhausted and the kernel is moving process pages to disk. The fix is either adding RAM or reducing process memory consumption. This page covers how to measure swap I/O, identify which process is swapping, and tune vm.swappiness to control when swap is used.
How Does Linux Swap Work and Why Does It Slow Web Servers?
Swap is disk space the kernel uses as an overflow area for RAM pages that are not currently needed. When a process needs more memory than is available in RAM, the kernel selects pages that have not been accessed recently (cold pages) and writes them to swap, freeing RAM for the process’s new allocation. When those swapped-out pages are needed again, the kernel reads them back from disk. On a spinning HDD, this swap read takes 5-20ms per page. On SSD, it takes 0.1-0.5ms. PHP process pages being swapped to HDD means PHP execution time increases from 100ms to 3 seconds — because every memory access for a swapped-out page triggers a disk read. The server does not crash (unlike OOM), but it slows to a crawl. The key metric is swap I/O rate, not swap usage. A server with 500MB of swap used but zero swap I/O is fine — the 500MB of pages are sitting on disk untouched (pages that were paged out during a quiet period and have not been needed since). A server with 100MB of swap used but high si (swap in) and so (swap out) in vmstat is actively swapping — those pages are being read and written continuously, causing disk I/O and process slowdowns. vm.swappiness controls how aggressively the kernel uses swap. Value 0-100: low values (1-10) make the kernel exhaust all available RAM before using swap; high values (60-100) cause the kernel to swap out idle pages proactively, keeping more RAM free for file system cache. For web servers, vm.swappiness=10 is standard — avoid swapping unless RAM is genuinely exhausted.
Tools and Commands
# Check current swap usage
free -h
swapon --show
# Monitor swap I/O rate (si=swap in, so=swap out, in KB/s)
vmstat 2 10
# si and so should be 0 almost all the time
# si > 0: pages being read from swap back into RAM (process was swapped, now active)
# so > 0: pages being written from RAM to swap (RAM pressure)
# Check vm.swappiness
cat /proc/sys/vm/swappiness
# Set vm.swappiness temporarily
sysctl -w vm.swappiness=10
# Persist across reboots
echo 'vm.swappiness = 10' >> /etc/sysctl.d/99-performance.conf
sysctl -p /etc/sysctl.d/99-performance.conf
# Identify which processes have pages in swap
for pid in $(ls /proc | grep '^[0-9]'); do
swap=$(awk '/VmSwap/{print $2}' /proc/$pid/status 2>/dev/null)
comm=$(cat /proc/$pid/comm 2>/dev/null)
[[ $swap -gt 0 ]] && echo "${swap}kB $comm (PID $pid)"
done 2>/dev/null | sort -rn | head -15vmstat’s si/so columns are the most important swap metrics for performance — they tell you if swapping is actively happening right now. The /proc loop shows which processes have pages currently on swap, ranked by swap usage.
Key Parameters
| Flag / Parameter | Description | Security Note |
|---|---|---|
vm.swappiness | Kernel tendency to swap anonymous pages (process heap/stack) vs reclaiming file cache pages. 0 = avoid swap until very last resort; 100 = swap aggressively. | Set to 10 for web servers. This tells the kernel to strongly prefer reclaiming file cache (cheap — files are re-read from disk on demand) over swapping process pages (expensive — process execution stalls during swap-in). Default 60 is too high for interactive web workloads. |
vmstat si (swap in) | Kilobytes per second being read from swap back into RAM. | Non-zero si means processes that were swapped are now being accessed — the server is recovering from a swap event. Persistent si > 0 means active swap churn: pages written to swap, accessed, read back, then swapped again. This causes continuous disk I/O and is the most damaging swap scenario. |
vmstat so (swap out) | Kilobytes per second being written from RAM to swap. | Non-zero so means RAM is under pressure and the kernel is actively evicting pages. so > 0 during peak traffic hours is the warning sign. so > 1000 KB/s indicates severe RAM shortage — reduce process memory or add RAM. |
vm.vfs_cache_pressure | Tendency to reclaim directory and inode cache entries. | Default 100. Increase to 200 on web servers with many PHP files — frees more memory from the dentry/inode cache for process use. This is an alternative to swapping when RAM is tight: reduce vfs_cache, not process RSS. |
Diagnosis and Fix Workflows
Determine if swap I/O is causing current slowness
Run vmstat and check si/so columns. If they are consistently above 0 during a performance complaint, swap I/O is contributing to the slowdown. Correlate with disk iostat await for the swap device.
# Run vmstat with 1-second intervals, watch si and so columns
vmstat 1 30
# Column layout:
# procs: r b
# memory: swpd free buff cache
# swap: si so
# io: bi bo
# system: in cs
# cpu: us sy id wa st
# Look for:
# swpd > 0 AND (si > 0 OR so > 0) = active swapping
# wa (cpu wait) > 20% correlating with si > 0 = swap I/O causing CPU wait
# Cross-reference with iostat to see which device is handling swap I/O
iostat -xz 1 10 | grep -E 'Device|sda|nvme'Reduce swap usage by identifying and fixing the RAM consumer
Active swapping means RAM is exhausted. Find the processes consuming the most RAM, then reduce their allocation or the number of instances.
# Find top RAM consumers
ps --no-headers -eo rss,pid,comm --sort=-rss | head -10 | awk '{printf "%.0fMB %s (PID %s)\n", $1/1024, $3, $2}'
# Compare to memory budget:
free -m
# If used >> available: over-provisioned processes
# Most common over-provisioned services on LAMP:
# 1. Apache prefork MaxRequestWorkers too high:
apachectl -V | grep MPM
grep MaxRequestWorkers /etc/apache2/mods-available/mpm_prefork.conf
# 2. PHP-FPM pm.max_children too high:
grep pm.max_children /etc/php/8.1/fpm/pool.d/www.conf
# 3. MySQL innodb_buffer_pool_size too large:
mysql -e 'SHOW VARIABLES LIKE "innodb_buffer_pool_size";'
# Reduce the largest consumer by 10-20% and re-measure free memoryClear swap without rebooting to confirm which process had pages swapped
Swapping swap off and back on forces all swap pages back into RAM — if RAM is available, this succeeds and the swapped pages return. Use this to clear swap after reducing process memory, then re-examine which processes previously had swap usage.
# Check current swap state
free -h
swapon --show
# Attempt to clear swap (will fail if not enough free RAM)
swapoff -a && swapon -a
# If swapoff fails with 'Cannot allocate memory':
# Not enough free RAM to bring back all swapped pages
# Reduce process memory first (restart PHP-FPM to recycle workers)
systemctl reload php8.1-fpm
sleep 60
swapoff -a && swapon -a
# After successful swapoff/swapon:
# Swap should be 0 and free RAM should be roughly what was swapped + prior free
free -hPerformance Impact: Active Swap I/O Degrades All Processes Simultaneously
Active swap I/O slows down every process on the server, not just the ones being swapped. The disk device is shared — when MySQL pages are being swapped back into RAM (si), the disk I/O subsystem is busy with those reads. Any other process that needs to read a file (PHP source files, nginx config, static assets) must wait behind the swap I/O in the same queue. This is why swap-induced slowdowns are particularly hard to diagnose: the symptom is global slowness that does not point to any single process. The vm.swappiness=10 setting mitigates this by making the kernel strongly prefer file cache reclamation over process swapping. File cache reclamation is cheap — if nginx later needs a file that was evicted from cache, it re-reads from disk as a normal sequential read. Process swapping is expensive — a PHP worker that was swapped needs its pages back before it can execute. The performance difference between file cache miss and process swap-in is 5-10x on HDD. On a server where swap is actively being used despite vm.swappiness=10, the fix is always the same: reduce the RAM footprint of the largest processes (MySQL buffer pool, PHP-FPM pool, Apache workers) until total RSS stays below available RAM with 200MB headroom. No amount of swappiness tuning helps when the server genuinely does not have enough RAM for the workload.
- vm.swappiness=0 does NOT disable swap — it means the kernel avoids swap until absolutely necessary. Use swapoff -a to fully disable swap (risky: OOM kill if RAM runs out).
- Swap on SSD is acceptable (0.1-0.5ms latency) as a safety net but should not be relied upon for normal operation — measure si/so regularly to detect when swap becomes active.
- swapoff -a requires sufficient free RAM to hold all currently-swapped pages — if RAM is fully occupied, swapoff will fail with ENOMEM. Reduce process memory before attempting swapoff.
- High swpd with si=0 and so=0 in vmstat is NOT a problem — those pages are parked on swap, not being accessed. Only active si/so causes performance impact.
Practical Examples
Set vm.swappiness permanently and verify
# Check current value
cat /proc/sys/vm/swappiness
# Set temporarily
sysctl -w vm.swappiness=10
# Persist
cat >> /etc/sysctl.d/99-webserver.conf << 'EOF'
# Prefer file cache reclamation over process swapping
vm.swappiness = 10
# Reduce inode/dentry cache pressure slightly
vm.vfs_cache_pressure = 100
EOF
sysctl -p /etc/sysctl.d/99-webserver.conf
# Verify
cat /proc/sys/vm/swappiness
# Should output: 10The sysctl.d directory is the correct location for persistent sysctl settings on systemd-based distributions. Settings in /etc/sysctl.d/ are applied at boot before network services start.
Monitor swap I/O rate continuously during a traffic peak
# Watch vmstat every 2 seconds, highlight active swap
watch -n 2 'vmstat 1 3 | awk "NR==1{print} NR==2{print} NR>2{if(\$7>0 || \$8>0) printf \"\\033[31m%s\\033[0m\\n\", \$0; else print}"'
# Alternatively use dstat for a more readable output:
apt install dstat
dstat --swap --disk --mem 2
# Shows real-time swap in/out, disk reads/writes, and free/used/cached memory
# in one view — much easier to correlate swap I/O with disk I/Odstat is more readable than vmstat for correlating swap I/O with disk I/O. Install it and run during the next reported performance issue to capture the exact conditions.
Troubleshooting Common Issues
Problem: Server is slow but vmstat shows si/so = 0 and swpd is non-zero
Solution: Non-zero swpd with zero si/so means pages are parked on swap but not being accessed — this is not causing the slowness. Look elsewhere: check CPU (mpstat), disk I/O (iostat), and network. The swpd number from a previous memory event is misleading the diagnosis.
Problem: vm.swappiness=10 set but server still swaps heavily
Solution: vm.swappiness=10 means the kernel prefers not to swap but will still swap if RAM is genuinely exhausted. The server has too many processes for its RAM. Reduce MaxRequestWorkers (Apache) or pm.max_children (PHP-FPM) and recalculate the memory budget. swappiness tuning alone cannot fix insufficient RAM.
Problem: swapoff -a fails with 'Cannot allocate memory'
Solution: There is not enough free RAM to absorb the currently-swapped pages. Reduce process memory first: systemctl reload php8.1-fpm (recycles workers and frees their accumulated RSS), wait 60 seconds, then retry swapoff -a. If it still fails, the server is chronically over-provisioned and needs more RAM.
Summary
Monitor swap I/O with vmstat — si and so columns show if swapping is actively happening. Non-zero si/so indicates RAM shortage causing performance degradation. Set vm.swappiness=10 to delay swap use until RAM is genuinely exhausted. Identify the largest RAM consumers with ps and reduce their allocation (MySQL buffer pool, PHP-FPM pool size, Apache MaxRequestWorkers). The root fix for chronic swapping is always reducing total process RSS below available RAM.
- Watch
siandsocolumns invmstat 2— non-zero values mean active swap I/O is degrading all processes; non-zeroswpdwith zero si/so is harmless. - Set
vm.swappiness=10in/etc/sysctl.d/99-webserver.conf— tells the kernel to strongly prefer file cache reclamation over process swapping; default 60 is too aggressive for web server workloads. - Active swapping always indicates RAM exhaustion — reduce the largest RAM consumer (MySQL
innodb_buffer_pool_size, PHP-FPMpm.max_children, or ApacheMaxRequestWorkers) until free RAM stays above 200MB under peak load.
Related Commands
what happens when swap is exhausted and OOM killer fires • memory leaks that grow to fill RAM and trigger swapping • swap I/O competing with MySQL and PHP for disk throughput • additional sysctl memory settings that complement vm.swappiness
Is Your Server Running at Full Performance?
INTRAM manages Linux servers with performance tuning built in from day one — correct MySQL configuration, PHP stack selection, nginx or Apache optimisation, and continuous monitoring so slowdowns are caught before users notice.
Explore Managed Hosting