Why the kernel swaps memory out even while free -h shows reserves
Several gigabytes of swap usage while free -h simultaneously reports plenty of free memory looks paradoxical, but is usually proactive, intentional kernel behavior rather than a fault. This guide shows how to use Pressure Stall Information, vmstat, and smaps to distinguish harmless from actually problematic swapping, what role swappiness really plays, and when MySQL buffer pool configuration is the actual cause.
Table of Contents
- 1. Why swap usage with free RAM isn't a contradiction
- 2. Reading free -h correctly: available is not the same as free
- 3. vmstat and si/so: measuring activity instead of a snapshot
- 4. Pressure Stall Information: measuring real memory pressure
- 5. Understanding and correctly placing swappiness
- 6. Using smaps to find out what was actually swapped out
- 7. MySQL and the buffer pool: a common special case
- 8. cgroup v2 memory pressure for container workloads
- 9. Normal vs. problematic swapping compared
- 10. Summary
- 11. FAQ
1. Why swap usage with free RAM isn't a contradiction
Seeing several gigabytes of occupied swap in free -h while plenty of free memory is reported at the same time looks like a bug at first glance. In fact, this behavior is intentional in most cases: the Linux kernel proactively swaps out rarely used memory pages once they have not been touched for a while, in order to make the freed-up physical memory available for the page cache and actively used application data. This strategy follows the basic assumption that unused RAM is wasted RAM, and prioritizes active data over rarely used but still allocated memory.
The actual problem only arises when exactly these swapped-out pages are needed again shortly afterward and the kernel has to fetch them back from the significantly slower swap partition under time pressure. This situation, known as thrashing, shows up as noticeable latency and high I/O wait times, even though free -h still reports plenty of free memory. The central challenge in diagnosis therefore lies not in establishing that swap is being used, but in distinguishing between harmless, proactive swapping out and actually harmful, repeated thrashing.
2. Reading free -h correctly: available is not the same as free
A common misinterpretation of free -h is confusing the free and available columns. The free column shows completely unused memory reserved for neither applications nor the page cache, while available additionally includes memory currently sitting in the page cache but that can be released immediately and without performance loss if needed. A low free value alongside a high available value is entirely normal and merely shows that the kernel is using memory efficiently for caching, not that a shortage is imminent.
What matters for assessing swap usage is watching the used column of the swap row over time instead of evaluating a single value. A swap value that has been stable for days and does not change points to a one-time swap-out event further in the past and is usually unproblematic. A swap value that grows continuously and simultaneously coincides with rising CPU time in the %wa field (I/O wait) of top, on the other hand, is a clear warning sign of active, burdensome swapping that is actually impairing application performance.
# free vs. available — the distinction that matters
free -h
# total used free shared buff/cache available
# Mem: 31Gi 18Gi 1.2Gi 412Mi 12Gi 13Gi
# Swap: 8.0Gi 2.1Gi 5.9Gi
# A stable swap value that hasn't changed in days is usually harmless
# Log it over time to distinguish a one-time event from ongoing pressure
watch -n 300 'free -h | grep Swap'
3. vmstat and si/so: measuring activity instead of a snapshot
While free -h only shows a momentary state, vmstat 1 provides actual swap activity in real time via the si (swap in, pages loaded back from the swap partition into RAM) and so (swap out, pages newly swapped out) columns. Consistently high values in both columns simultaneously are the decisive signal for thrashing: the kernel swaps pages out and has to fetch them back shortly afterward, which points to a real memory shortage where the available RAM simply is not enough for the active workload.
Occasional short spikes in so without corresponding si activity, on the other hand, are unproblematic and correspond exactly to the proactive swapping out of rarely used pages described in the previous section. The rule of thumb: a one-time spike in so followed by silence is normal, a recurring pattern of alternating si and so over several minutes is a warning sign that justifies a deeper investigation of memory requirements.
# Live view of swap in/out activity, refreshed every second
vmstat 1 10
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
# r b swpd free buff cache si so bi bo in cs us sy id wa st
# 2 1 2150400 125600 45200 12800000 0 0 120 340 1200 2400 12 4 82 2 0
# 3 4 2151200 118400 45200 12790000 45 180 890 1200 1800 3100 18 9 55 18 0
# ^^^ ^^^ — sustained si+so = real thrashing
# Aggregate view without the noise: only si/so columns over time
vmstat 1 60 | awk '{print $7, $8}'
4. Pressure Stall Information: measuring real memory pressure
Pressure Stall Information (PSI), available since kernel 4.20 under /proc/pressure/memory, is the most precise tool available for directly quantifying memory pressure instead of inferring it from proxies such as swap usage. PSI measures what percentage of time at least one process (some) or all processes simultaneously (full) were blocked due to missing memory, aggregated over 10 seconds, 60 seconds, and 5 minutes. A full avg10 value near zero means that despite swap usage, practically no process was noticeably slowed down by memory shortage, which classifies the previously observed swap activity as unproblematic.
If the full avg10 value rises above a low single-digit percentage range, applications spend measurable time blocked waiting for swapped-out pages currently being fetched back. In practice, this single value replaces the tedious manual correlation between vmstat activity, application latency, and CPU wait time, and should be the first thing to check whenever a swap problem is suspected, even before free -h or vmstat.
# The single most reliable indicator of actual memory pressure
cat /proc/pressure/memory
# some avg10=2.34 avg60=1.87 avg300=0.95 total=48291823
# full avg10=0.12 avg60=0.08 avg300=0.03 total=1204958
# ^^^^^^^^^ — near-zero "full" means no real stalling
# Alert threshold suggestion: full avg10 consistently above 5% warrants investigation
awk '/^full/ {print $2}' /proc/pressure/memory | grep -oP 'avg10=\K[0-9.]+'
5. Understanding and correctly placing swappiness
The vm.swappiness parameter (default usually 60) controls how aggressively the kernel prioritizes swapping out memory pages versus the page cache when memory becomes scarce, not whether swapping happens at all. A common misconception is assuming that vm.swappiness=0 disables swap entirely. In fact, since kernel 3.5, a value of 0 only means that swap is used exclusively as a last resort, shortly before the OOM killer would have to intervene, while values between 1 and 100 set the relative weighting between evicting page cache and swapping out application memory.
For database servers like MySQL, where the server's own buffer pool already represents the most effective caching layer, a lower value such as vm.swappiness=10 is often sensible, because it signals to the kernel to favor the page cache more strongly rather than swapping out actively used application memory in favor of file system caching. A swappiness value alone, however, does not solve any underlying capacity problem; it only shifts which type of memory is affected first when pressure actually rises.
# /etc/sysctl.d/99-swappiness.conf
# Lower value favors keeping application memory resident,
# at the cost of evicting page cache more aggressively under pressure.
# Recommended starting point for database servers (MySQL/MariaDB).
vm.swappiness = 10
sudo sysctl --system
# Verify the active value
cat /proc/sys/vm/swappiness
6. Using smaps to find out what was actually swapped out
Once PSI confirms real memory pressure, the next step is finding out which process is responsible for the swapped-out pages. /proc/[pid]/status provides the per-process swapped-out memory amount via VmSwap, while smem -st swap provides an overview of all processes sorted by swap usage. A process with a strikingly high VmSwap value relative to its VmRSS indicates that a substantial part of its memory has not been actively used for a while, which can be entirely normal for a database server or a PHP-FPM master process with rarely used but allocated buffers.
For an even more precise analysis, /proc/[pid]/smaps shows the swap portion per individual memory mapping, which helps distinguish between swapped-out heap memory, swapped-out shared memory segments, and swapped-out library mappings. This breakdown is particularly valuable when there is a suspicion that a specific application component, for example a rarely used admin area of a Magento installation, allocates an unnecessary amount of memory that is then rightfully swapped out, rather than there being an actual configuration problem.
# Per-process swap usage, sorted highest first
smem -st swap
# PID User Command Swap USS PSS RSS
# 1823 mysql /usr/sbin/mysqld 512.0M 890.2M 920.4M 1024.8M
# 4821 www-data php-fpm: master process 128.4M 12.1M 18.9M 22.4M
# Breakdown of swapped memory within a single process's mappings
grep -B2 "^Swap:" /proc/1823/smaps | grep -A2 "^7f" | head -30
7. MySQL and the buffer pool: a common special case
For MySQL servers, swap usage is a particularly common and at the same time particularly critical topic, because the InnoDB buffer pool is deliberately configured to occupy a large portion of available RAM in order to minimize disk access. If innodb_buffer_pool_size is sized too generously, for example at 80 percent of total memory on a server that also runs PHP-FPM workers and other services, the buffer pool actively competes with these services for physical memory, and the kernel starts swapping out rarely used buffer pool pages.
The actual problem here: swapped-out buffer pool pages are useless to MySQL once they are actually needed for a query, because fetching them back from the swap partition is orders of magnitude slower than a direct disk access via InnoDB itself would have been. In this case, the solution is not adjusting swappiness but correctly sizing innodb_buffer_pool_size while accounting for all other services on the same server, typically at 60 to 70 percent of available RAM instead of a blanket 80 percent rule that ignores co-located services.
8. cgroup v2 memory pressure for container workloads
In containerized environments, cgroup v2 provides the same PSI metric as the system-wide /proc/pressure/memory via memory.pressure, but isolated to the respective cgroup, which is particularly valuable for Docker containers and systemd services with their own memory limit. A container that reaches its memory.max limit starts swapping (unless memory.swap.max is set to 0) or gets terminated by the cgroup OOM killer, independent of the host's overall memory state. The system-wide free -h output can look entirely unremarkable while a single container is already under considerable internal memory pressure.
For Magento setups with multiple containers on the same host, targeted monitoring of memory.pressure per cgroup is therefore more informative than the system-wide view, because it shows precisely which individual service is actually under pressure, rather than providing an aggregated value that can mask local bottlenecks.
# Per-cgroup memory pressure, isolated from the rest of the host
cat /sys/fs/cgroup/system.slice/docker-<container-id>.scope/memory.pressure
# Current memory limit and swap configuration for a specific cgroup
cat /sys/fs/cgroup/system.slice/docker-<container-id>.scope/memory.max
cat /sys/fs/cgroup/system.slice/docker-<container-id>.scope/memory.swap.max
9. Normal vs. problematic swapping compared
The overview below summarizes the key signals for distinguishing between harmless and actually problematic swapping.
| Signal | Normal | Problematic |
|---|---|---|
| Swap used (free -h) | Stable over days, no change | Continuously growing |
| vmstat si/so | One-time so spike, then quiet | Sustained si/so alternation |
| PSI full avg10 | Near 0 percent | Clearly above 5 percent |
| Application latency | Unchanged | Noticeably elevated, correlates with so |
Only when several of these signals fall into the problematic column simultaneously, especially an elevated PSI value combined with sustained si/so activity, is actual thrashing present that justifies a capacity increase or a memory configuration adjustment. A stable swap value viewed in isolation, without these accompanying signals, requires no action and is normal, efficient kernel behavior instead.
Mironsoft
Linux memory analysis and database tuning for Magento infrastructure
Swap alerts without actual memory pressure?
We analyze your memory usage with PSI and vmstat, distinguish harmless from actually problematic swapping, and size MySQL buffer pool and swappiness to match your real workload.
Memory Pressure Audit
PSI-based analysis of whether swap usage is actually slowing down applications
MySQL Tuning
Buffer pool sizing that accounts for all services running on the server
Capacity Planning
Reliably separating real bottlenecks from harmless, proactive swapping
10. Summary
High swap usage despite free RAM in free -h is, in most cases, not a bug but proactive kernel behavior that swaps out rarely used memory pages in favor of the page cache and actively used data. The decisive question is not whether swap is being used, but whether this swapping actually leads to noticeable thrashing. Pressure Stall Information under /proc/pressure/memory provides the most precise, directly interpretable metric for this, complemented by sustained si/so activity in vmstat as confirmation.
Swappiness only influences which type of memory is affected first, but does not solve a capacity problem itself. For database servers, correctly sizing innodb_buffer_pool_size while accounting for all co-located services is often more important than any swappiness adjustment. Anyone combining PSI, vmstat, and smaps reliably distinguishes between normal, efficient memory behavior and an actual bottleneck that justifies a capacity increase.
Troubleshooting High Swap Usage: The Essentials at a Glance
Baseline behavior
The kernel proactively swaps out rarely used pages to prioritize page cache and active data.
Key metric
PSI full avg10 from /proc/pressure/memory shows directly whether processes are actually being slowed down.
Swappiness role
Only controls priority between page cache and application memory, does not solve a capacity problem.
MySQL special case
Size innodb_buffer_pool_size correctly instead of treating swappiness as a quick fix.