Swappiness Misconfigured: Symptoms, Diagnosis & Fixes
One little knob decides how eagerly your server trades hot memory for slow disk. The default isn't always right.
What It Is
Swappiness is a single number, between 0 and 100 (or, on recent kernels, up to 200), that tells the kernel how willing it should be to swap. It lives at /proc/sys/vm/swappiness, the default on almost every Linux box is 60, and most people who run servers never touch it, never look at it, and have no idea it exists — right up until the day a database that should be flying is mysteriously stuttering, and the answer turns out to be this one line nobody set on purpose.
So let's be precise about what the knob actually does, because the popular explanation — "higher means more swapping" — is true but shallow, and the shallow version is exactly what leads people to set it wrong. The kernel, when it needs to free up RAM, always has two kinds of pages it could reclaim: anonymous pages (a program's actual working memory — its heap, its stack, the data it's computing on) and file-backed pages (the page cache — copies of files it read off disk to keep them handy). Reclaiming an anonymous page means swapping it out to disk, because that data exists nowhere else. Reclaiming a file-backed page is nearly free — the file is already on disk, so the kernel just drops the cached copy. Swappiness is the dial that sets the relative preference between those two moves. Low: cling to anonymous memory, drop cache first. High: be just as happy to swap your program's working memory out as to drop a cached file.
And that's the whole reason this page exists rather than a one-line "set it to 10": the right value depends entirely on what the box does, and the wrong value is a silent latency tax, not an outage. A latency-sensitive database server with plenty of RAM wants a low swappiness, so its hot working set stays resident in fast memory and never gets shuffled out to disk under it. A general-purpose box is fine on the default. And the trap that catches the clever — setting it to 0 thinking it means "never swap" — does not mean that on a modern kernel, and can actually hasten the out-of-memory killer. By the end of this page you'll read the current value in one command, know exactly which number fits your workload, make it stick across reboots, and understand the 0-isn't-zero nuance that bites people who think they're being careful. We'll start where it helps — how you notice, how to read it, how to fix it — and save the surprisingly elegant why (how Linux reclaims memory at all) for the end.
How You Notice
Misconfigured swappiness is the quietest problem in this whole corner of the knowledge base. It doesn't throw an error, it doesn't kill a process, it doesn't fill a disk. It just makes a box slightly, persistently wrong — and the symptoms are easy to blame on everything else first. Here's each tell, with the command to check it on your own box right now.
-
The value itself is the symptom. Unlike most problems on this site, this one you diagnose by reading a setting, not by watching damage accumulate. One command shows you where the dial is pointed:
cat /proc/sys/vm/swappinessIf that reads
60on a RAM-heavy database server, or anything above10on a latency-sensitive box, you've found a tuning gap — not a crisis, but a number worth a second look. (This is exactly the line CleverUptime reads to raise its finding; more on that below.) -
A box swaps with RAM clearly to spare. The classic fingerprint of swappiness set too high. You run
free -h, see a chunk of memory sitting inSwap, glance at theMemline — and findavailableis roomy. The box had no need to swap; it chose to, because the dial told it cache was worth more than keeping your program resident.free -hSwap used with available RAM to spare is the signature: not a memory full shortage, a preference gone wrong. (When swap fills with RAM also gone, that's a different page — swap full.)
-
Latency spikes that don't line up with load. The subtle, maddening one — how most people actually meet this without ever naming it. A query that's usually 2 ms occasionally takes 200 ms; a request tail-latency graph grows a fat 99th percentile; the box "feels fine" on averages but stutters under the hood. What's happening is that a hot page got swapped out during a quiet moment, and the next time the program touched it, that access had to wait for a disk read instead of a memory read. With swappiness too high, the kernel keeps making that trade against you. The CPU looks idle, the box looks healthy, and yet the feel is off — because the cost is hiding in I/O wait on pages that never should have left RAM.
-
Pages drift into swap and stay there. Watch swap creep up over days on a box that's nowhere near full. A little swap is healthy (see swap full for why), but on a high-swappiness box the creep is faster and reaches deeper into pages that are actually warm — and every one of those is a future latency spike waiting for its program to come asking.
None of these is a 3 a.m. emergency, and that's the point: this is a tuning problem, an Info-or-Warning, not an outage. But left wrong it taxes every request the box serves, forever, for no reason — and the fix is one line. Any of these tells means the same first move: read the current value, decide what the workload wants, and set it deliberately.
How I Read It
The diagnosis here is refreshingly direct — you read a number and a couple of supporting gauges. There are two ways to ask the kernel where the dial is set, and they print it slightly differently:
cat /proc/sys/vm/swappiness
60
That's the rawest possible read: the file /proc/sys/vm/swappiness is the setting — reading it returns the live value, writing to it changes the kernel's behaviour on the spot. (There's the first quiet bit of magic, and we'll come back to it: this isn't a config file the kernel consults — it's a window straight onto a live kernel variable.) The friendlier front door is sysctl, which prints the same number with its full dotted name:
sysctl vm.swappiness
vm.swappiness = 60
Same value, two faces. So the box above reads 60 — the universal default. Whether that 60 is fine or wrong depends on one thing and one thing only: what this server is for. That's the entire judgement, so let's make it concrete.
What the Number Means
The cleanest mental model, and the one straight from the kernel's own documentation, is this: swappiness is "the rough relative IO cost of swapping and filesystem paging." At 100, the kernel assumes swapping a page out to disk and dropping-then-re-reading a cached file cost the same, and so it treats anonymous memory and page cache as equally fair game when it needs to reclaim. Lower values tell the kernel "swap IO is expensive — prefer dropping cache." Higher values say "swap IO is cheap — go ahead and swap." It is not a percentage of anything, and it is not an on/off switch; it's a thumb on a scale the kernel weighs every time it reclaims.
Here's the practical translation, which is what you actually act on:
0— Swap as little as possible. The kernel won't initiate swapping until free and file-backed memory drops below a low internal threshold (the zone's high watermark). This is not "never swap" — a critical nuance with its own callout below.1— The "almost never, but you may if you truly must" value. The recommended floor for latency-sensitive servers precisely because it isn't0— it keeps swap as a genuine last-resort safety valve without inviting the OOM trouble that0can.10— The sane server default a lot of admins reach for: keep the working set in RAM, swap only under real pressure. If you tune nothing else, this is the value to know.60— The kernel's out-of-the-box default. Tuned for a balanced desktop-ish workload where caching aggressively is worth the occasional swap-out. Fine for a general box; often too eager for a dedicated database.100— Equal preference: swap anonymous memory as readily as drop cache. Rarely what a server wants.100–200(newer kernels only) — Bias toward swapping over evicting cache. This range only makes sense when swap is genuinely cheap — compressed in-RAM swap likezramorzswap, or swap on faster storage than your data filesystem. On a box that swaps to a normal disk, treat100as the practical ceiling.
Note
The range used to be
0–100, full stop. Modern kernels extended the maximum to200to support setups where swap is faster than the filesystem — compressed RAM swap (zram,zswap) or an NVMe swap device in front of slower data storage — where it can genuinely be cheaper to swap than to re-read a file. If you've never set up compressed swap, ignore everything above100; it does nothing useful on a plain disk-swap box and only invites confusion. The number that matters for almost every server lives in the1–60band.
Read It Next to the Memory Gauges
The swappiness value on its own only tells you the policy; to know whether that policy is hurting you you read it beside the actual memory picture, with free -h:
free -h
total used free shared buff/cache available
Mem: 31Gi 9.2Gi 512Mi 128Mi 21Gi 21Gi
Swap: 8.0Gi 1.4Gi 6.6Gi
Read it as a verdict, not a hedge: this box, if it's a database server, has swappiness set wrong. Look at the evidence together. available RAM is a luxurious 21Gi out of 31Gi — there is no memory shortage here, none. And yet 1.4Gi is sitting in Swap. The kernel had 21 gigabytes of headroom and still chose to push 1.4 GB of some program's working memory out to disk — because at swappiness 60 it values keeping that fat 21Gi of page cache just as much as keeping your application resident. On a latency-sensitive box that's a bad trade: every one of those swapped pages is a future stall. (Confirm there's no actual churn with vmstat — si/so near zero means the pages are parked, not thrashing; high and sustained is swap thrashing, a real emergency that no swappiness setting fixes.)
Now contrast the same gauges on a box where the dial is set right for a database:
total used free shared buff/cache available
Mem: 31Gi 24Gi 612Mi 128Mi 6.1Gi 6.4Gi
Swap: 8.0Gi 0B 8.0Gi
sysctl vm.swappiness on this one reads 1. The database has been allowed to keep 24Gi of working memory resident, Swap used is a flat 0B, and the page cache has been trimmed to 6.1Gi to make room — exactly the trade you want on a machine whose job is fast queries, not fast file reads. Same hardware, same workload, one knob, completely different latency story.
Reading It by Example
Train the pattern-match. Readout on the left, what I'd actually conclude on the right:
vm.swappiness = 60, general-purpose box,free -hshows little or no swap used → The default, doing its job. Nothing to change. Most boxes look exactly like this and want nothing more.vm.swappiness = 60, dedicated database/cache server,Swapholding 1–2 Gi with gigabytes ofavailableRAM → Too high for this workload. The kernel is swapping hot pages it didn't need to. Lower it to10, or1for a strict latency target.vm.swappiness = 10(or1),Swapused0B,availableRAM healthy → Tuned right for a server. The working set stays in RAM; swap waits in reserve as a safety valve. Leave it.vm.swappiness = 0, and the box has started OOM-killing with swap nearly empty → The0trap. The kernel is refusing to swap until it's almost too late, then reaping a process instead. Raise it to1— see the Danger callout.vm.swappiness = 100+ on a box with plain disk swap → Misconfigured the other direction: biased toward swapping when swap is slow. Unless you're runningzram/zswap, bring it down into the1–10band.vm.swappiness = 10butvmstat 1showssi/sopinned high anyway → Not a swappiness problem at all. The box is genuinely out of RAM and thrashing; no dial value saves you. That's swap thrashing / out-of-memory — fix the shortage, not the knob.
That last row is the most important distinction on the page: swappiness tuning fixes eager swapping with RAM to spare. It does nothing for swapping caused by an actual memory shortage. If the box is truly out of memory, lowering swappiness just trades a slow swap-grind for a faster OOM kill. Know which one you're looking at before you touch the dial — available RAM is the tell, every time.
How to Fix It
The fix is genuinely one line — the skill is choosing the right line and making it survive a reboot. But first, the one trap on this whole page that can turn a tuning tweak into an outage:
Danger
vm.swappiness=0does not mean "never swap" on any kernel since 3.5 (back-ported into RHEL/CentOS and most distros years ago). It means "defer swapping until memory is critically low" — and that change has a sharp edge: on a box that briefly spikes past its RAM, a swappiness of0makes the kernel refuse to use the swap sitting right there and instead invoke the out-of-memory killer to reap a process. People who set0believing it was the safe, swap-proof choice have watched it kill their database with swap nearly empty. If you want "swap only as an absolute last resort," the correct value is1, never0— it keeps swap as a genuine safety valve. Reserve0for the rare case where you have measured, understood, and truly want to forbid swap, and you've sized RAM so the OOM killer never has cause to fire.
With that understood, change the value. Two steps: live now, persistent forever.
Set it live (takes effect instantly, lost on reboot):
sysctl -w vm.swappiness=10
That writes straight through to the kernel — no restart, no service reload, the new policy is in force the moment the command returns. (You can prove it: sysctl -w just does the same thing as echo 10 > /proc/sys/vm/swappiness, writing to that live window onto the kernel variable.) But a sysctl -w change evaporates on the next reboot, so on its own it's only a test, not a fix.
Make it persistent (survives reboots):
The right home for a permanent kernel-tunable is a small file in /etc/sysctl.d/, which the system reads at boot. Create one named clearly so the next person knows what it's for:
echo 'vm.swappiness = 10' > /etc/sysctl.d/99-swappiness.conf
sysctl --system
sysctl --system re-reads every config file (the /etc/sysctl.conf file plus everything in /etc/sysctl.d/) and applies them immediately, so this single command both persists the setting and activates it in one go — no reboot needed to confirm it took.
Pro Tip
Prefer a dedicated drop-in file in
/etc/sysctl.d/(like99-swappiness.conf) over editing the big shared/etc/sysctl.confdirectly. Drop-ins are applied in filename order (hence the99-prefix to win over earlier files), they survive package upgrades that might rewrite the main file, and — the part you'll thank yourself for later — one purpose-named file makes it instantly obvious what was changed and why, instead of one mystery line buried in a hundred-line config. The same/etc/sysctl.d/pattern is how you persist every kernel tunable, not just this one.
Now, which value? Pick by what the box actually does:
- Dedicated database, cache, or any latency-sensitive server (lots of RAM, hot working set):
1, or10if you want a touch more cache-friendliness. Keep the working set resident; treat swap as an emergency reserve, not a routine parking lot. MySQL, PostgreSQL, Redis, Elasticsearch and the like all run better here — their own caches are smarter about your data than the kernel's generic page cache is. - General-purpose server (web app, mixed workload, moderate RAM):
10–30, or just leave the60default if it isn't causing trouble. Most boxes never need to think about this. - Memory-tight box where you'd rather swap than die: keep a meaningful value (
60, even higher), and add swap so there's a real overflow tank — see swap full for sizing. Here you want the kernel comfortable using swap, because the alternative is the OOM killer. - Desktop or workstation: the
60default is genuinely well-chosen — interactive responsiveness benefits from a generous page cache, and the occasional swap-out of an idle background app is invisible. Don't reflexively crank it down to1on a laptop because a database blog told you to; you'll just evict cache you wanted.
How to Avoid It
Swappiness is the rare server problem you can put right once and never revisit, because — unlike a filling disk or a dying drive — the value doesn't drift on its own. Set it deliberately at provisioning time and it stays put. In rough order of payoff:
- Set it on purpose, in config management. Bake the right
vm.swappinessinto your provisioning — the/etc/sysctl.d/drop-in baked into your image, Ansible/Terraform/cloud-init, whatever builds your boxes — so every server is born with a value chosen for its role instead of inheriting60by accident. The whole problem is really just "nobody decided," so deciding is the cure. - Match the value to the role, not to a blog post. The single biggest mistake is cargo-culting
vm.swappiness=1onto every box because a database tuning guide said so. A1on a memory-tight general server starves the cache and can push it toward the OOM killer; a60on a dedicated database taxes every query. The value is a workload decision — make it per role, the way you'd size RAM per role. - Right-size RAM first; swappiness is the polish, not the fix. A low swappiness keeps a healthy box's working set resident — it cannot conjure memory that isn't there. If a server is genuinely short on RAM, no value of this dial helps; you'll just swap less and OOM sooner. Provision enough memory for the working set, then tune swappiness to keep it resident. (If you're swapping because you're out of RAM, you're on the wrong page — go to swap full or out-of-memory.)
- Never set
0"to be safe." It's the well-intentioned mistake that backfires, for the reason in the Danger callout above. If your instinct is "I never want this box to swap," the safe expression of that instinct is1plus enough RAM, not0.
Swappiness is just one knob in a whole panel of virtual-memory tunables under vm.* — vm.dirty_ratio and vm.dirty_background_ratio (how much dirty page cache piles up before the kernel flushes it to disk), vm.vfs_cache_pressure (how eagerly it reclaims directory and inode caches), vm.overcommit_memory (how freely it hands out memory it doesn't have). They all live in the same /etc/sysctl.d/ place and read the same way via sysctl. Swappiness is the one worth knowing first because it's the one most often left wrong — but the moment you've tuned it deliberately, you've learned the pattern for every other VM knob on the box.
How Memory Reclaim Actually Works
Now the part you don't need to fix anything — but that turns this from "set it to 10 and move on" into a genuine sense for what your kernel is doing every second, and it happens to be one of the more elegant corners of how Linux works. To understand swappiness you have to understand the one thing it's a knob on: page reclaim, the kernel's constant, quiet work of deciding what to keep in your finite RAM and what to throw out. Once you can picture that, the whole page above stops being rules to memorise and becomes something you can simply reason out.
Two Kinds of Page, One Hard Choice
Every byte of memory the kernel manages belongs, for reclaim purposes, to one of two families — and the entire idea of swappiness rests on the difference between them.
The first family is file-backed pages: the page cache. When a program reads a file, the kernel keeps a copy of those blocks in RAM, because reading the same file again from memory is thousands of times faster than going back to disk. Crucially, the disk still has the original. So when the kernel needs to free a file-backed page, reclaiming it is almost free: if the cached copy is clean (unchanged since it was read), the kernel simply forgets it — drops the page, no writing required, and re-reads it from disk later only if anyone asks. Cheap to evict, cheap to get back.
The second family is anonymous pages: a program's own private working memory — the heap it malloc'd, its stack, the variables it's computing on right this instant. The word "anonymous" is the key: these pages are backed by no file. They exist only in RAM. So if the kernel wants to reclaim an anonymous page to free that RAM, it has nowhere to drop it — it must first write it out to swap, or the data is simply gone. That write is the swap-out. And getting it back later means a swap-in: a disk read, a page fault, a program stalled mid-instruction waiting for its own memory to come home.
There's the asymmetry the whole page hangs on. Evicting a clean file-backed page costs essentially nothing. Evicting an anonymous page costs a disk write now and a disk read later, and stalls a program when it returns. So when the kernel is under memory pressure and has to reclaim something, it faces a real choice with real, unequal costs — and swappiness is literally the number that biases that choice. Low swappiness: "anonymous memory is precious, lean on the cache." High swappiness: "treat them evenly, I'd rather keep a big cache." It's not a vague aggression dial; it's the thumb on a specific, concrete scale, weighing two specific costs, millions of times a day.
Why the Kernel Swaps a Box That Isn't Full
Here's the thing that confuses everyone the first time, and it dissolves the instant you hold the picture above: why would a kernel swap when there's free RAM? Because — and this is one of the quietly profound ideas in operating-system design — the kernel does not consider free RAM a virtue. Empty RAM is wasted RAM: a gigabyte sitting idle is a gigabyte not caching a hot file, not buffering I/O, not making anything faster. So the kernel actively wants to put memory to work, and it's perpetually making a trade: "this anonymous page hasn't been touched in ages — if I swap it out, I free a frame I can spend on cache that something's reading right now." At swappiness 60, it judges that trade worth making fairly readily. So a box with 21 GB free will still nudge some genuinely-idle anonymous pages out to swap, because the kernel reasoned the reclaimed RAM was better spent on cache. From the kernel's point of view that's not a bug — it's optimisation. From a latency-sensitive application's point of view it can be a quiet disaster, because "idle" pages have a way of turning out to be needed a millisecond after they're evicted. Lowering swappiness is you telling the kernel: on this box, you're wrong about that trade — keep my working set resident, I'll forgo the extra cache.
Why
Internally, the kernel doesn't store your
60and check it directly. It folds swappiness into a calculation that compares the relative "pressure" on the anonymous and file LRU lists — the least-recently-used lists it keeps for each family, so it can evict cold pages before warm ones. Swappiness shifts the balance point between those two scans: roughly, the cost it assigns to scanning (and thus reclaiming from) anonymous memory versus file cache. That's why the kernel docs phrase it as a "relative IO cost" rather than a percentage — under the hood it's exactly that, a weight in a formula, and100is the point where the two costs are declared equal. The dial you set is a coefficient in the kernel's own arithmetic.
Where 60 Comes From, and Why It's Often Wrong for Servers
The default of 60 is not arbitrary, and knowing its origin tells you exactly when to override it. It was chosen for a general-purpose, interactive machine — the kind of box where a fat page cache makes the whole system feel snappy (programs launch faster, files open instantly) and where the occasional swap-out of some idle background process is completely invisible to the person at the keyboard. For that workload, 60 is genuinely well-judged: lean a bit toward caching, accept a little swapping, feel fast.
A dedicated server flips half of those assumptions. A database doesn't want the kernel managing its hot data as generic page cache — it has its own buffer pool that understands its access patterns far better than the kernel's blind LRU ever could, and it would much rather that memory stay pinned as its own working set than be swapped out so the kernel can cache something the database will never re-read. There's no human at a keyboard for whom responsiveness needs that generous cache. And a 200 ms latency spike from a swapped-in page, invisible on a desktop, is a blown SLA on a query server. So the very reasoning that makes 60 right for a laptop makes it wrong for a database — which is why the single most common swappiness fix in the world is "a server inherited the desktop default, and nobody told it it wasn't a desktop."
So hold the whole shape of it: the kernel has finite RAM and two kinds of page to evict, one cheap and one costly; it reclaims constantly to keep memory useful rather than empty; swappiness is the weight it uses to choose between the cheap eviction and the costly one; and the default leans toward caching because it was born on machines that benefit from caching. Set the dial to match what your box is, and the kernel's relentless, invisible bookkeeping starts working with your workload instead of against it. The knob was never mysterious — it's just a single number standing in for one of the oldest hard questions in computing: of all the things I could keep in fast memory, which do I throw out first?
See Also
sysctl— read and setvm.swappinessand every other kernel tunablefree— see RAM and swap together; the gauge that tells you if the dial is hurting youvmstat— thesi/socolumns that show whether swap is churning or just parkedtop— find what's resident and what's been pushed out; pressMto sort by memoryps—ps aux --sort=-%memfor the memory leaderboard in a snapshot- /etc/sysctl.conf — where kernel tunables like swappiness are made permanent
- /proc/meminfo — the kernel's raw memory ledger behind
free - swap — what swap actually is, partition vs file, and how the kernel uses it
- page cache — the file-backed memory swappiness weighs against your working set
- virtual memory — the bigger idea that makes swapping possible at all
- swap full — when the overflow tank itself runs dry: usage, not policy
- swap thrashing — when swap churns: the real emergency no dial fixes
- out-of-memory — where a genuine shortage ends, and why
0can hasten it - memory full — the RAM side of the squeeze, judged by
available
Is your server swapping hot memory to disk for no reason — just because nobody set this one knob?
CleverUptime reads
vm.swappinesson every server you run it on and flags when it's set higher than the workload wants, in plain language, so you fix the silent latency tax once instead of chasing phantom slowdowns that never show up in the CPU graph.Want to see your own server's health right now? One command, no signup, no install.