40.5 VM Tuning: vm.dirty_ratio, vm.overcommit_memory
Right, let’s talk about the kernel’s virtual memory (VM) subsystem. This is where we go from userspace tourists to kernel-level operators. The kernel’s VM is a brilliant, complex, and occasionally slightly unhinged piece of engineering. It’s trying to juggle a thousand things at once: making your applications feel fast, using your RAM efficiently, and preventing the whole house of cards from collapsing. The sysctl knobs we’re about to tweak are how we whisper suggestions into the juggler’s ear. Use this power wisely.
The core dilemma the VM faces is that physical RAM is finite, but applications are greedy and have dreams of infinite memory. The kernel uses your disk as a giant, slow overflow area—this is “swap.” Our first two parameters control how the kernel handles two critical parts of this balancing act: how it manages promises of memory (overcommit_memory) and how it decides to actually clean its room and take out the trash (dirty_ratio).
The Overcommit Gambit
vm.overcommit_memory is a philosophical setting. It dictates how the kernel answers a simple question from a program: “Can I have, say, 8 gigabytes of memory, pretty please?”
The default setting on most distributions is 0, meaning “Heuristic overcommit.” This is the kernel saying, “Yeah, probably, I’ll figure it out later. Trust me.” It uses a simple algorithm (the ‘heuristic’) to guess if the request is probably reasonable. This works fine 99% of the time because most programs ask for huge chunks of memory “just in case” and never actually use it all (a concept called “memory overcommitment”). It’s efficient.
Set this to 1 and the kernel becomes a pushover. It always says “Yes.” to every memory request, regardless of how absurd it is. This is “Always overcommit.” The only limit is the sum of RAM plus swap. It’s like giving your credit card to a teenager in a casino. You might be fine, or you might get a very alarming phone call later (in this case, the OOM Killer ringing to say it’s murdered your favorite processes to free up memory).
Now, set it to 2. This is the kernel putting on its accountant glasses and saying, “No. You may only commit up to the sum of swap plus a defined percentage of RAM.” This is “Don’t overcommit.” It’s the safest, most conservative option, and it’s why some Java and other memory-intensive applications demand it. They’d rather get an obvious “malloc() failed” error at the moment of request than have the entire system randomly keel over later when the kernel’s lie is exposed.
To see your current setting and the resulting commit limit:
sysctl vm.overcommit_memory
sysctl vm.overcommit_ratio # This is the 'percentage of RAM' used in mode 2
cat /proc/meminfo | grep CommitLimit
If you need to be absolutely sure your memory-intensive, mission-critical workload never gets OOM-killed, you switch to mode 2. You’re trading a small chance of a random application crash (the OOM Killer) for a guaranteed, predictable crash of the specific application that asks for too much. It’s a trade-off.
The Dirty Laundry Problem
Let’s talk about dirt. When the kernel writes data to a file, it doesn’t immediately rush to the slow disk. That would be terribly inefficient. Instead, it writes the data to a fast cache in RAM first. This data in RAM that’s newer than what’s on disk is “dirty.” The vm.dirty_ratio and vm.dirty_background_ratio control when the kernel finally does the laundry.
Think of vm.dirty_background_ratio as your helpful roommate. When the dirty pages reach this percentage of total available memory, a kernel thread (flusher) quietly starts in the background, writing the oldest dirty data to disk. Your application doesn’t wait for this; it just keeps running, generating new dirty data.
Now, vm.dirty_ratio is the fire alarm. If the background flusher can’t keep up and the dirty data reaches this higher percentage of memory, things get serious. The kernel forces all application writes to block—they have to stop and wait for the flusher to make some room. Your smooth, responsive system suddenly feels janky and slow. This is what we call “I/O wait,” and it’s a sign your ratios are probably too high for your workload.
Check your current settings:
sysctl vm.dirty_ratio vm.dirty_background_ratio
# Typical defaults might be 20 and 10, respectively.
So why would you ever change these? Performance tuning. On a system with a beastly battery-backed write cache (like a high-end RAID controller or a super-fast NVMe drive), you can afford to get sloppy. You can raise these ratios way up (say, 60 and 20) because you know you can flush that huge pile of dirty data to the “disk” incredibly fast when you need to. This allows writes to be aggregated into huge, efficient chunks.
Conversely, if you’re running on a Raspberry Pi with a slow SD card, you want to be more aggressive, not less. Lower these values (maybe 10 and 5). You can’t handle a big pile of dirty data, so you want the background flusher to start early and often, preventing the painful blocking that happens at the higher ratio. The best practice is to monitor your I/O wait (using iostat or vmstat) and your dirty data size:
cat /proc/meminfo | grep Dirty
If you see Dirty consistently climbing high before being written, and you’re experiencing latency, your ratios are too high for your I/O subsystem. Lower them. It’s that simple. Don’t just copy some blog post’s values; understand what they mean for your hardware.