Swap File vs Swap Partition
Swap partitions were the historical default because early Linux kernels had performance penalties with file-based swap. Since kernel 2.6 and more visibly since 4.x, a swap file on ext4 or XFS performs identically to a partition in benchmark conditions. On our test server running fio sequential write tests, the throughput difference between a swap partition and a swap file on XFS was under 1%.
The practical argument for swap files in 2026 is flexibility. You can resize a swap file without repartitioning. On cloud instances where you get a single root volume and no secondary block device, a swap file is the only option without re-imaging. Swap partitions still make sense on embedded systems or setups where the filesystem is read-only, and on systems using LVM where adding a logical volume for swap is trivial.
One exception: Btrfs. Do not place a swap file on a Btrfs subvolume unless you are on kernel 5.0+ and the file was created with the no-copy-on-write attribute. Even then, we have seen data corruption edge cases in older Btrfs versions. On Btrfs, use a swap partition or a loop device on a separate file.
# Check if Btrfs is your root filesystem before creating a swapfile
findmnt -n -o FSTYPE /
Creating a Swap File
The canonical method on modern kernels uses fallocate to preallocate the file, then mkswap to format it. On XFS and ext4, fallocate creates a fully allocated file without holes, which is required for swap.
Set permissions to 600 immediately. A world-readable swap file leaks memory contents. We have seen this misconfiguration on otherwise well-hardened servers.
After swapon, verify the swap is active with swapon --show, which gives you the type, size, used space, and priority. Add the entry to /etc/fstab to survive reboots. The defaults option in fstab is sufficient; swap does not honor most mount options.
# Create a 4GB swap file
fallocate -l 4G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# Verify
swapon --show
# Add to fstab
echo '/swapfile none swap defaults 0 0' >> /etc/fstab
Creating a Swap Partition with LVM
On a server with LVM already managing storage, adding swap as a logical volume takes under a minute. This approach integrates with your existing VG capacity management and makes resizing straightforward later.
After mkswap and swapon, add it to /etc/fstab using the UUID rather than the device path. Device paths like /dev/vg0/swap are stable in LVM but UUID-based entries are more portable if you ever migrate the VG.
To get the UUID of a newly created swap volume, run blkid /dev/vg0/swap and copy the UUID value.
# Assumes VG named vg0 with free space
lvcreate -L 8G -n swap vg0
mkswap /dev/vg0/swap
swapon /dev/vg0/swap
# Get UUID for fstab
blkid /dev/vg0/swap
# Add to fstab (replace UUID with actual value)
echo 'UUID= none swap defaults 0 0' >> /etc/fstab
Swap Priority and Multiple Swap Devices
Linux supports multiple simultaneous swap spaces and distributes pages across them round-robin when priorities are equal. Higher priority swap is used first and exhausted before lower priority swap is touched.
Set priority with the pri= option in fstab or the -p flag to swapon. Use a higher priority number for your fastest device. If you have an NVMe SSD and a spinning HDD in the same system, put the swap file on the NVMe at priority 10 and the HDD swap at priority 1. The kernel will fill NVMe swap first.
When two devices share the same priority, the kernel interleaves pages across both in round-robin, which can improve throughput on systems with multiple independent storage controllers. We tested this with two separate NVMe devices on a Threadripper workstation and saw ~15% improvement in swap write throughput under artificial memory pressure from stress-ng.
Check active priorities with swapon --show, which shows the Pri column.
# Activate swap with explicit priority
swapon -p 10 /swapfile
swapon -p 1 /dev/sdb1
# fstab equivalent
# /swapfile none swap defaults,pri=10 0 0
# /dev/sdb1 none swap defaults,pri=1 0 0
# Show active swap with priorities
swapon --show
Tuning swappiness
The vm.swappiness kernel parameter controls how aggressively the kernel moves anonymous memory pages to swap vs reclaiming page cache. The default is 60 on most distributions. At 60, the kernel will start swapping relatively eagerly. At 10, it strongly prefers evicting page cache before touching anonymous memory.
For a database server like PostgreSQL or MySQL where the database manages its own cache and you want the OS page cache left alone, set swappiness to 10. For a general-purpose application server where you want some protection against OOM but minimal swap activity, 20-30 is reasonable. Never set it to 0 on production systems - this disables swap for anonymous pages entirely and guarantees OOM kills when you spike past RAM capacity.
The vm.swappiness = 1 setting, popular in some Redis documentation, keeps swap available as a last resort without the kernel proactively using it. We use this on Redis nodes ourselves.
Changes via sysctl are immediate and do not require a restart. The sysctl.d drop-in file persists across reboots.
# Check current value
cat /proc/sys/vm/swappiness
# Set temporarily
sysctl vm.swappiness=10
# Persist across reboots
cat > /etc/sysctl.d/99-swap.conf << 'EOF'
vm.swappiness = 10
vm.vfs_cache_pressure = 50
EOF
sysctl --system
vfs_cache_pressure and Its Interaction with Swap
The vm.vfs_cache_pressure parameter controls how aggressively the kernel reclaims memory used for VFS caches - specifically dentry and inode caches. The default is 100, which means the kernel reclaims these at the same rate as other cached data. Values below 100 make the kernel prefer keeping these caches in memory; values above 100 cause aggressive reclaim.
On a system with large directory trees and frequent stat() calls - think a build server or a server running millions of small files - inode and dentry cache pressure matters. Setting vfs_cache_pressure to 50 allows these caches to persist longer, reducing repeated disk reads. On our CI build server, dropping vfs_cache_pressure from 100 to 50 reduced build times by about 8% on a cold cache run with 50,000 source files.
The interaction with swap: if vfs_cache_pressure is too low, dentry/inode caches grow large, crowding out application memory, which then spills to swap. If it is too high, those caches get reclaimed, increasing disk I/O for metadata operations. For most servers with a mix of file operations and in-memory application state, 50 is a safe starting point.
# Check current dentry and inode cache sizes
cat /proc/slabinfo | grep -E '^(dentry|inode_cache)'
# Or use slabtop for a live view
slabtop -o | head -20
Monitoring Swap Usage in Detail
The free -h command gives totals but hides which processes are contributing to swap pressure. For production diagnosis, you need per-process swap data.
The /proc/
The vmstat command shows swap I/O in the si (swap-in) and so (swap-out) columns. Nonzero so values mean the kernel is actively writing pages to swap. Nonzero si values mean pages are being read back from swap into RAM - the more expensive operation. Sustained si activity is a signal that the system is thrashing and needs either more RAM or workload reduction.
For DevOps teams building automated swap monitoring, tools like taskbotshub.ai can trigger alerts or remediation scripts when swap thresholds are crossed, integrating with existing CI/CD pipelines and on-call workflows.
# Per-process swap usage, sorted descending
for pid in /proc/[0-9]*/status; do
awk -v pid="${pid%/status}" '/VmSwap/{if($2>0) print pid, $2, $3}' "$pid"
done | sort -k2 -rn | head -20
# vmstat showing swap I/O (1 second intervals, 10 samples)
vmstat 1 10
# /proc/meminfo for full picture
grep -E '(Swap|MemAvailable)' /proc/meminfo
Resizing a Swap File
You cannot resize a swap file in place while it is active. The process is: swapoff, delete, recreate at the new size, mkswap, swapon. This means you need enough free RAM plus existing swap to absorb the pages currently in swap during the swapoff. If the system is already under memory pressure, swapoff will fail with ENOMEM.
If swapoff fails, the workaround is to add a second temporary swap file at higher priority, wait for the kernel to migrate pages from the original swap to the new one, then swapoff the original.
For LVM-based swap, resizing is cleaner: swapoff, lvresize, mkswap (re-format required after resize), swapon.
# Resize swap file from 4G to 8G
swapoff /swapfile
rm /swapfile
fallocate -l 8G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# LVM swap resize
swapoff /dev/vg0/swap
lvresize -L 16G /dev/vg0/swap
mkswap /dev/vg0/swap
swapon /dev/vg0/swap
zswap: Compressed Swap Cache in RAM
zswap is a kernel feature that intercepts pages being written to swap and compresses them into a pool of RAM instead. Only if the RAM pool fills up do pages actually get written to disk swap. On workloads with compressible data (which most application memory is), zswap can reduce disk swap I/O by 60-80% while using a fraction of the memory that would have been swapped.
zswap is enabled at boot via kernel parameter or via sysfs. The zstd compressor is the best choice on kernels 6.0+, offering better compression ratios than lzo and faster decompression than lz4. The z3fold allocator is more memory-efficient than zbud for the pool pages themselves.
Set the max pool size as a percentage of RAM. We use 20% on servers where the workload has bursty memory allocation. On a 32GB server, that is 6.4GB of compressed swap cache before anything hits disk.
Check zswap statistics in /sys/kernel/debug/zswap/ - the pool_total_size and stored_pages counters tell you how much RAM zswap is using and how many pages it holds.
# Enable zswap at runtime (kernel 6.x)
echo 1 > /sys/module/zswap/parameters/enabled
echo zstd > /sys/module/zswap/parameters/compressor
echo z3fold > /sys/module/zswap/parameters/zpool
echo 20 > /sys/module/zswap/parameters/max_pool_percent
# Persist via kernel command line (add to GRUB_CMDLINE_LINUX in /etc/default/grub)
# zswap.enabled=1 zswap.compressor=zstd zswap.zpool=z3fold zswap.max_pool_percent=20
# After editing grub config
update-grub # Debian/Ubuntu
grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL/Fedora
# Check zswap stats
grep -r '' /sys/kernel/debug/zswap/ 2>/dev/null
Swap and Containers: What Actually Happens
Docker containers on a Linux host share the host's swap. By default, a container's memory limit via --memory does not restrict swap usage - you need --memory-swap to set the combined memory+swap limit. If you set --memory=2g without setting --memory-swap, the container can use up to 2GB RAM plus 2GB swap (double the memory limit).
To disable swap for a container entirely, set --memory-swap equal to --memory. To allow unlimited swap, set --memory-swap to -1.
In Kubernetes, swap support was alpha in 1.28 and reached beta in 1.30. As of Kubernetes 1.32, you can enable swap for pods by setting memorySwap.swapBehavior to LimitedSwap or UnlimitedSwap in the kubelet config. LimitedSwap allocates swap proportional to the pod's memory request relative to node capacity. This is the safer option for multi-tenant clusters.
For nodes running Kubernetes, set vm.swappiness to 0 if swap is disabled entirely (the historical recommendation), or to 10 if you are using the beta swap support. Never leave it at the default 60 on a Kubernetes node - it causes unpredictable latency for latency-sensitive pods.
# Docker: limit container to 2GB RAM, 4GB total (2GB swap)
docker run --memory=2g --memory-swap=4g myapp
# Docker: disable swap for a container
docker run --memory=2g --memory-swap=2g myapp
# Check container's current memory+swap usage
docker stats --no-stream --format 'table {{.Name}}\t{{.MemUsage}}'
# Kubernetes: check kubelet swap configuration
kubectl get node -o jsonpath='{.status.nodeInfo.kubeletVersion}'
Swap in systemd: systemd-swap and Zram
On systems running systemd 248+, you can manage swap through systemd units. The systemd-swap package (separate from systemd itself, available on most distributions) automates swap file creation and zswap/zram configuration from a single config file at /etc/systemd/swap.conf.
Zram is distinct from zswap. Zram creates a block device backed by compressed RAM with no disk backing at all. Pages in zram are lost if the system runs out of RAM entirely - there is no overflow to disk. Zram is appropriate for systems where you want fast swap for temporary working set expansion but disk swap is too slow to be useful (embedded systems, low-end VPS with slow disk I/O). Modern Android devices use zram as their sole swap mechanism.
On Fedora 33+ and RHEL 9+, zram is the default swap via the systemd-zram-setup@zram0.service unit. On Debian and Ubuntu, you need to install the zram-tools package or configure it manually.
Do not run both zswap and zram simultaneously. They serve the same purpose and combining them wastes RAM on double compression.
# Check if zram is active
lsmod | grep zram
cat /proc/swaps
# Manual zram setup (if not managed by systemd-zram-setup)
modprobe zram
# Set size to 4GB
echo 4G > /sys/block/zram0/disksize
mkswap /dev/zram0
swapon -p 100 /dev/zram0 # High priority, use before disk swap
# Disable zswap if using zram
echo 0 > /sys/module/zswap/parameters/enabled
Swap Sizing Recommendations by Workload
The old rule of 2x RAM is meaningless on modern servers. The right swap size depends on what you are protecting against.
For hibernation: swap must be at least the size of physical RAM, plus the memory footprint of the kernel itself (add ~10% headroom). Without sufficient swap, hibernation silently fails or corrupts.
For crash recovery and OOM avoidance: 4-8GB of swap on servers with 32GB+ RAM is the practical floor. You want enough swap to absorb a single runaway process or an unexpected traffic spike while you respond. A 256GB RAM server does not need 256GB of swap - it needs enough to survive the time between an alert firing and a sysadmin responding, typically 10-30 minutes.
For desktop and developer workstations: match RAM up to 8GB, then use 8GB flat above that. A workstation with 64GB RAM does not benefit from 64GB of swap.
For Kubernetes nodes with swap disabled: zero swap, but ensure node has memory limits set on all pods and that the kubelet's --eviction-hard thresholds are configured to evict pods before OOM kills happen.
# Check current RAM and calculate a reasonable swap size
free -g
# Rule of thumb: min(RAM, 8G) for servers with 32GB+ RAM
# For hibernation: swap >= RAM + 10%
# Verify hibernation will work (checks resume= parameter and swap size)
cat /sys/power/image_size
grep -i resume /proc/cmdline