$linuxjunkies
>

Linux Virtual Memory Tuning

Tune Linux virtual memory for production: control overcommit_memory, swappiness, min_free_kbytes, and use drop_caches safely to reduce OOM kills and swap pressure.

AdvancedUbuntuDebianFedoraArch9 min readUpdated June 1, 2026

Before you start

  • Root or sudo access on the target system
  • Basic familiarity with Linux memory concepts (RAM, swap, page cache)
  • Baseline performance metrics collected before tuning (vmstat, free -h)
  • A test or staging environment recommended before applying to production

The Linux kernel's virtual memory (VM) subsystem controls how RAM is allocated, when swap is used, how aggressively the page cache is reclaimed, and what happens when processes request more memory than physically exists. Mis-tuned VM parameters are behind a surprising number of production performance problems—OOM kills on underloaded machines, swap storms on database servers, or latency spikes caused by aggressive page cache flushing. This guide covers four critical tunables: overcommit_memory, swappiness, drop_caches, and min_free_kbytes.

How Linux Virtual Memory Works (the Short Version)

Linux separates virtual address space from physical RAM. Processes get virtual pages; the kernel maps those to physical frames on demand. The page cache sits in whatever RAM isn't used by processes, speeding up disk I/O. When physical memory runs low, the kernel's OOM killer starts terminating processes—unless swap is available to push cold pages out first. The tunables below let you control each phase of this pipeline.

Viewing Current VM Parameters

All VM knobs live under /proc/sys/vm/ and are also settable via sysctl. Always check the current value before changing anything.

sysctl vm.swappiness vm.overcommit_memory vm.min_free_kbytes

Sample output (will vary):

# vm.swappiness = 60
# vm.overcommit_memory = 0
# vm.min_free_kbytes = 67584

overcommit_memory

vm.overcommit_memory controls whether the kernel grants memory allocations that exceed what is physically available (RAM + swap). It has three modes:

  • 0 (heuristic, default) — The kernel uses an algorithm to decide. Most malloc() calls succeed even if RAM is tight; it refuses obviously unreasonable requests.
  • 1 (always overcommit) — Every allocation succeeds regardless of available memory. Useful for workloads that mmap large sparse files (e.g., some scientific computing or Redis with SAVE disabled).
  • 2 (never overcommit) — The kernel refuses allocations that would exceed swap + (overcommit_ratio % of RAM). This is the safe choice for systems where an OOM kill is worse than an allocation failure—databases, financial systems.

Setting overcommit_memory

For a database server where OOM kills are unacceptable, use mode 2 with a conservative ratio:

sysctl -w vm.overcommit_memory=2
sysctl -w vm.overcommit_ratio=80

overcommit_ratio (default 50) defines what percentage of physical RAM contributes to the commit limit under mode 2. Setting it to 80 allows total committed memory up to swap + 80% of RAM. Tune this to match your actual workload's peak RSS.

To check how much of your commit limit is in use:

grep -E 'CommitLimit|Committed_AS' /proc/meminfo

swappiness

vm.swappiness (0–200 on kernels ≥ 5.8, 0–100 on older kernels) biases the kernel toward reclaiming anonymous pages (heap, stack) versus file-backed pages (page cache). A higher value means the kernel swaps anonymous memory more readily, preserving page cache. A lower value keeps anonymous memory in RAM longer, at the cost of shrinking page cache.

  • Default (60): reasonable for desktops and general servers.
  • 10: common recommendation for database servers (PostgreSQL, MySQL). Keeps working sets in RAM; avoids swap latency.
  • 0: do not swap anonymous pages unless there is no alternative. Not the same as disabling swap—the kernel can still swap under extreme pressure.
  • 1: the practical minimum for "almost never swap" without the edge-case risks of 0.
  • 200 (kernel ≥ 5.8): always prefer reclaiming anonymous pages. Useful on memory-constrained systems with zswap or zram where swap is cheap.

Changing swappiness at Runtime

sysctl -w vm.swappiness=10

On systems with zram (common on Fedora 33+, Ubuntu 22.10+, Chrome OS), the recommended swappiness is 100–200 because zram compression is cheaper than leaving pages in RAM unreferenced. Check if zram is active:

zramctl

Making Changes Persistent

Runtime sysctl -w changes reset on reboot. Persist them via /etc/sysctl.d/.

cat > /etc/sysctl.d/99-vm-tuning.conf <<'EOF'
vm.swappiness = 10
vm.overcommit_memory = 2
vm.overcommit_ratio = 80
vm.min_free_kbytes = 131072
EOF

sysctl --system

sysctl --system reloads all drop-in files without rebooting. This works identically on Debian/Ubuntu, Fedora/RHEL, and Arch.

min_free_kbytes

vm.min_free_kbytes sets a hard floor of free memory (in KiB) that the kernel tries to keep available at all times for critical kernel allocations—interrupt handlers, network stack, and device drivers that cannot sleep waiting for memory. If free RAM drops below this threshold, the kernel aggressively reclaims pages.

The default is calculated at boot based on RAM size (roughly the square root of RAM in KiB, capped). On systems with large amounts of RAM or high-bandwidth network cards (10 GbE+), the default is often too low and causes transient allocation failures or network drops under load.

Choosing a Value

  • Servers ≤ 4 GB RAM: 65536 (64 MB) is typically fine.
  • Servers 8–64 GB RAM: 131072–262144 (128–256 MB). A common production value for busy PostgreSQL hosts.
  • Servers > 64 GB RAM: 524288–1048576 (512 MB–1 GB). High-throughput network servers benefit from more headroom.
  • Do not set this above 5% of total RAM: the kernel treats pages below this threshold as unreclaimable, which paradoxically triggers OOM kills on systems with ample RAM.
# Check current value and total RAM for context
sysctl vm.min_free_kbytes
grep MemTotal /proc/meminfo
sysctl -w vm.min_free_kbytes=262144

drop_caches

/proc/sys/vm/drop_caches is a write-only trigger that instructs the kernel to free page cache, dentries, and inodes. It is not a tunable—it is a one-shot operational tool. Do not write to it in production scripts or cron jobs; doing so discards warm cache and causes a cold-cache performance hit for every disk read that follows.

Legitimate uses: benchmarking (to get consistent cold-cache measurements), freeing memory before a large batch job, or reproducing a cache-cold scenario in testing.

  • 1: free pagecache only.
  • 2: free dentries and inodes only.
  • 3: free pagecache, dentries, and inodes.
# Sync first to avoid losing dirty pages
sync
echo 3 > /proc/sys/vm/drop_caches

Always run sync first. Writing to drop_caches only frees clean pages; dirty pages are not dropped, but syncing first ensures they're written out and become clean (and thus eligible to be freed).

Verification

After applying changes, confirm they took effect and observe the memory state:

sysctl vm.swappiness vm.overcommit_memory vm.overcommit_ratio vm.min_free_kbytes
free -h && cat /proc/meminfo | grep -E 'MemFree|Cached|SwapUsed|CommitLimit|Committed_AS'

On a running system, watch reclaim activity in real time:

vmstat 2 10

The si and so columns (swap-in, swap-out) should be near zero on a well-tuned server under normal load.

Troubleshooting

OOM kills despite free-looking memory

Check Committed_AS versus CommitLimit in /proc/meminfo. If you're using overcommit_memory=2 and Committed_AS is close to or over CommitLimit, new allocations fail and may trigger OOM. Either raise overcommit_ratio, add swap, or reduce memory pressure from the application.

System still swapping with swappiness=10

Check if swap is on a slow device with swapon --show. Also verify with vmstat 1 whether it's swap-out pressure (so) or swap-in (si); persistent swap-in on a workload that barely fits in RAM means you need more RAM, not a lower swappiness.

Sysctl changes not persisting after reboot

Verify the file landed in /etc/sysctl.d/ and not /etc/sysctl.conf (both work, but check for typos):

sysctl --system --dry-run 2>&1 | grep vm

On RHEL/Rocky 8+, SELinux contexts on custom sysctl.d files are sometimes wrong after manual creation; run restorecon /etc/sysctl.d/99-vm-tuning.conf if changes don't apply.

tested on:Ubuntu 24.04Fedora 40Rocky 9Arch 2024.05

Frequently asked questions

Does setting swappiness=0 completely disable swap?
No. Swappiness=0 tells the kernel to avoid swapping anonymous pages unless absolutely necessary, but the kernel will still use swap under extreme memory pressure. To disable swap entirely, run 'swapoff -a' and remove swap entries from /etc/fstab.
Is it safe to run drop_caches on a production server?
It's safe in that it won't corrupt data, but dropping the page cache causes every subsequent disk read to be a cold-cache miss, potentially causing a severe temporary performance drop. Avoid automating it; use it only for benchmarking or one-time memory recovery.
Why do my sysctl changes disappear after reboot even though I wrote to /proc/sys/vm/ directly?
Writes to /proc/sys/ are runtime-only and lost on reboot. Persist changes in /etc/sysctl.d/99-vm-tuning.conf (or /etc/sysctl.conf) and reload with 'sysctl --system'.
What is the difference between overcommit_ratio and overcommit_kbytes?
Both only apply under overcommit_memory=2. overcommit_ratio sets the commit limit as a percentage of RAM (default 50%), while overcommit_kbytes sets an absolute KiB value instead. If overcommit_kbytes is non-zero, it takes precedence over overcommit_ratio.
Should I tune these differently on a system using zram?
Yes. With zram, swap is backed by compressed RAM rather than disk, so it is much faster. Raise swappiness to 100 or higher (kernel ≥ 5.8 allows up to 200) so the kernel actively uses zram to free RAM for the page cache, which is usually the right trade-off.

Related guides