<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Kernel · XB Field Notes</title><link>https://www.xbcatm.com/tags/kernel/</link><description>Illustrated field notes on HTTP, DNS, TLS, Caddy, SSH, releases, and recovery.</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://www.xbcatm.com/tags/kernel/index.xml" rel="self" type="application/rss+xml"/><item><title>Linux overcommit is a policy the kernel bet on</title><link>https://www.xbcatm.com/posts/linux-memory-overcommit-policy/</link><pubDate>Sat, 18 Dec 2027 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/posts/linux-memory-overcommit-policy/</guid><description>Overcommit lets malloc sell more virtual memory than physical RAM, betting those pages are never all touched at once, which is why allocation rarely fails on Linux.</description><content:encoded>&lt;p&gt;Tuning vm.overcommit_memory shifts the failure point from a blind heuristic back to your own budget, which changes exactly who eats an OOM kill.&lt;/p&gt;
&lt;h2 id="why-malloc-rarely-fails"&gt;Why malloc rarely fails&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;vm.overcommit_memory&lt;/code&gt; defaults to &lt;code&gt;0&lt;/code&gt;, the heuristic mode that says yes to almost every &lt;code&gt;mmap&lt;/code&gt; and &lt;code&gt;malloc&lt;/code&gt; while it can plausibly honor them, and only refuses when the system is genuinely short on memory. Committing virtual memory costs almost nothing up front because pages are handed out lazily, so most allocations succeed and most normal applications stay alive at near-zero cost. The price of the generosity is that the kernel can over-commit far beyond RAM, betting that you will not touch every page; when many bytes actually get written, someone has to pay with an OOM kill.&lt;/p&gt;
&lt;h2 id="the-knob"&gt;The knob&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;vm.overcommit_memory&lt;/code&gt; lets you choose who holds the budget. The default &lt;code&gt;0&lt;/code&gt; is heuristic overcommit: the kernel refuses only obviously impossible requests and otherwise says yes, trusting most processes never touch every byte they reserve. Setting it to &lt;code&gt;1&lt;/code&gt; means always overcommit: almost any allocation is granted, which suits huge sparse mappings that stay mostly untouched, but every failure is pushed downstream to reclaim or the OOM killer. Setting it to &lt;code&gt;2&lt;/code&gt; means strict accounting: any allocation beyond &lt;code&gt;CommitLimit&lt;/code&gt;, roughly swap plus &lt;code&gt;overcommit_ratio&lt;/code&gt; percent of RAM (&lt;code&gt;overcommit_kbytes&lt;/code&gt; takes over when nonzero), fails with &lt;code&gt;ENOMEM&lt;/code&gt; at request time. Neither extreme is free: always-overcommit hides pressure until the machine stalls, and strict mode breaks programs that reserve generously but touch little.&lt;/p&gt;
&lt;h2 id="heuristic-versus-strict"&gt;Heuristic versus strict&lt;/h2&gt;
&lt;p&gt;The real choice is who pays when the reckoning arrives. In heuristic mode the kernel keeps over-committing and, under pressure, picks a victim to kill; in strict mode the boundary is drawn in advance, so the failure surfaces at &lt;code&gt;malloc&lt;/code&gt; time and the operator, not the scheduler, decides which process eats an OOM kill. Containers and VMs typically set a controlled value because a host over-committing on behalf of many guests can collapse when their aggregate demand spikes. A laptop can be generous; a production box that must not silently lose a worker should be explicit about its budget.&lt;/p&gt;
&lt;h2 id="when-it-breaks"&gt;When it breaks&lt;/h2&gt;
&lt;p&gt;Under the default heuristic mode, the most confusing failure is a large &lt;code&gt;malloc&lt;/code&gt; or &lt;code&gt;mmap&lt;/code&gt; that returns success and then dies later. No pages were touched yet, so the allocation looked fine; once the process writes into them and memory runs short, the OOM killer picks a victim, often not the over-allocator but the largest nearby process. The log shows &lt;code&gt;Out of memory: Killed process&lt;/code&gt; with no failed allocation in sight, so teams chase the wrong binary while the real over-reservation stays hidden.&lt;/p&gt;
&lt;p&gt;Mode &lt;code&gt;1&lt;/code&gt;, always overcommit, fails slower. Absurd reservations are granted without protest, pressure builds silently, and a machine with swap slides into a swap storm where everything is alive but nothing progresses. Latency collapses before any kill frees memory, sessions stall, and the eventual OOM kill looks random because too many processes hold promises the system could never keep together.&lt;/p&gt;
&lt;p&gt;Mode &lt;code&gt;2&lt;/code&gt;, strict accounting, fails at request time instead. A JVM reserving a large heap up front, or a &lt;code&gt;fork&lt;/code&gt;-heavy service briefly needing twice its resident set accounted, gets &lt;code&gt;ENOMEM&lt;/code&gt; at startup even though it would never touch every page. Operators who copy a strict &lt;code&gt;overcommit_ratio&lt;/code&gt; from a swapless laptop onto a database host watch healthy programs refuse to start; size the ratio against the gap between &lt;code&gt;CommitLimit&lt;/code&gt; and &lt;code&gt;Committed_AS&lt;/code&gt; in &lt;code&gt;/proc/meminfo&lt;/code&gt; instead of flipping the mode blindly.&lt;/p&gt;
&lt;h2 id="check-it-quickly"&gt;Check it quickly&lt;/h2&gt;
&lt;p&gt;Save the following as &lt;code&gt;overcommit_probe.py&lt;/code&gt; and run &lt;code&gt;python3 overcommit_probe.py&lt;/code&gt;. It uses only the standard library, opens the &lt;code&gt;overcommit_*&lt;/code&gt; knobs and &lt;code&gt;/proc/meminfo&lt;/code&gt; read-only, changes nothing, and needs no privileges. Without those proc files it prints &lt;code&gt;SKIP&lt;/code&gt; instead of failing.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Read-only overcommit audit: report mode, budget, and pressure.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;OSError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;UNREADABLE: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;meminfo_kb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;partition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;digits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isdigit&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/proc/sys/vm/overcommit_memory&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/proc/sys/vm/overcommit_ratio&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;kbytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/proc/sys/vm/overcommit_kbytes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;meminfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/proc/meminfo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;meminfo&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;SKIP: /proc/sys/vm/overcommit_memory or /proc/meminfo missing (non-Linux?)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;meminfo_kb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;meminfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;overcommit_memory=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; overcommit_ratio=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; overcommit_kbytes=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kbytes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;MemTotal=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; kB SwapTotal=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; kB CommitLimit=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; kB Committed_AS=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; kB&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;MemTotal&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;SwapTotal&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;CommitLimit&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Committed_AS&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;heuristic&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;always overcommit&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;strict accounting&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;mode means: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;unknown value&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;CommitLimit&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;committed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Committed_AS&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;suggestion: heuristic default; watch Committed_AS growth, prefer mode 2 for workers that must not be OOM-killed&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;suggestion: always overcommit; expect swap storms under pressure, prefer 0 or 2 unless sparse mappings need it&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;committed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;headroom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;committed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;commit headroom=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; kB; suggestion: enlarge ratio/kbytes if headroom is small, shrink them if headroom is huge&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;headroom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;suggestion: unexpected mode value; compare Committed_AS against CommitLimit before changing anything&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On a default workstation the script reports &lt;code&gt;overcommit_memory=0&lt;/code&gt; and names it heuristic. Under mode &lt;code&gt;2&lt;/code&gt;, compare the headroom: &lt;code&gt;Committed_AS&lt;/code&gt; near &lt;code&gt;CommitLimit&lt;/code&gt; explains startup &lt;code&gt;ENOMEM&lt;/code&gt;, while huge headroom means the ratio is merely conservative. Under mode &lt;code&gt;1&lt;/code&gt; the suggestion warns about swap storms, matching the slow-then-random failure above.&lt;/p&gt;
&lt;h2 id="further-reading"&gt;Further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.xbcatm.com/posts/oom-killer-makes-the-final-memory-call/"&gt;The OOM killer makes the final memory call&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.xbcatm.com/posts/swap-gives-memory-a-slow-extra-layer/"&gt;Swap gives memory a slow extra layer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.kernel.org/doc/html/v6.13/admin-guide/sysctl/vm.html"&gt;Kernel vm sysctl documentation&lt;/a&gt;, defines the overcommit_memory modes and defaults, checked 2026-06-01.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man5/proc.5.html"&gt;proc manual page&lt;/a&gt;, documents /proc/meminfo CommitLimit and Committed_AS accounting, checked 2026-06-01.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>sysctl tunes the running kernel</title><link>https://www.xbcatm.com/posts/sysctl-tunes-the-running-kernel/</link><pubDate>Sun, 22 Nov 2026 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/posts/sysctl-tunes-the-running-kernel/</guid><description>A kernel is full of counters and switches. sysctl lets an operator read and change them without a reboot, then persist a few favorites.</description><content:encoded>&lt;p&gt;sysctl is not a configuration file format. It is a live view of the kernel&amp;rsquo;s parameter table, reachable by dotted name and mirrored under &lt;code&gt;/proc/sys&lt;/code&gt;, so reading and writing a value is literally reading and writing a pseudo-file. That is why &lt;code&gt;sysctl -w&lt;/code&gt; takes effect immediately and why nothing set that way survives a reboot.&lt;/p&gt;
&lt;p&gt;The tension is all in the second half: persistence. The runtime interface is honest and immediate, while the file meant to make a setting stick is subject to filename precedence, an implementation split over &lt;code&gt;/etc/sysctl.conf&lt;/code&gt;, and a failure mode where a line quietly does nothing.&lt;/p&gt;
&lt;h2 id="a-key-is-a-path-into-a-live-table"&gt;A key is a path into a live table&lt;/h2&gt;
&lt;p&gt;Every dotted key maps to one pseudo-file, &lt;code&gt;net.ipv4.ip_forward&lt;/code&gt; to &lt;code&gt;/proc/sys/net/ipv4/ip_forward&lt;/code&gt;, and sysctl(8) notes that procfs is required. &lt;code&gt;sysctl -a&lt;/code&gt; enumerates the table, skipping deprecated and verboten parameters, and one exclusion is a warning in itself: &lt;code&gt;stat_refresh&lt;/code&gt; returns no value but flushes per-CPU VM statistics, so reading everything is not always free.&lt;/p&gt;
&lt;p&gt;Because it is a live table rather than stored configuration, the key set is stable and the values are not: on the macOS host used for the fixture, &lt;code&gt;sysctl -a&lt;/code&gt; returned 1917 keys across 14 namespaces, and two consecutive reads produced an identical key set while 47 lines changed value. Reads and writes share one handler: an unknown key gives ENOENT, a read-only key refuses the write, and an unparseable value gives EINVAL. No daemon holds state.&lt;/p&gt;
&lt;h2 id="runtime-first-persistence-second"&gt;Runtime first, persistence second&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;sysctl -w key=value&lt;/code&gt; applies to the running kernel and writes nothing anywhere, which makes it the right tool for testing. Persisting means a file the init system reads, and there the two implementations disagree: procps &lt;code&gt;sysctl --system&lt;/code&gt; reads &lt;code&gt;/etc/sysctl.d&lt;/code&gt;, &lt;code&gt;/run/sysctl.d&lt;/code&gt;, &lt;code&gt;/usr/local/lib/sysctl.d&lt;/code&gt;, &lt;code&gt;/usr/lib/sysctl.d&lt;/code&gt; and &lt;code&gt;/lib/sysctl.d&lt;/code&gt;, then &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; last, while systemd-sysctl reads the drop-in directories and, as sysctl.conf(5) states, does not use &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; at all.&lt;/p&gt;
&lt;p&gt;Precedence is by filename, not directory: all configuration files are sorted lexicographically regardless of where they live, a same-named file in an earlier directory masks the later one, and for a given key the lexicographically latest filename wins. Hence the convention of a two-digit prefix and a dash, 10-40 for vendor files and 60-90 for local ones, so the local file sorts last regardless of which directory the reader walked first.&lt;/p&gt;
&lt;h2 id="the-failure-is-a-line-that-quietly-did-nothing"&gt;The failure is a line that quietly did nothing&lt;/h2&gt;
&lt;p&gt;sysctl.d(5) splits errors into two classes, and the split is the whole story. A permission error, or an attempt to write a variable that is not present on the local system, is logged at debug level and does not fail the service; other errors are louder and do cause a failure. So a line for a module-gated key, such as &lt;code&gt;net.bridge.bridge-nf-call-iptables&lt;/code&gt; before &lt;code&gt;br_netfilter&lt;/code&gt; is loaded, is dropped quietly and &lt;code&gt;sysctl --system&lt;/code&gt; still exits zero. The page&amp;rsquo;s remedy is to load the module first, via a udev rule or &lt;code&gt;modules-load.d&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The second quiet failure is ordering. The value in effect is whatever the last writer set, so a container runtime or a unit&amp;rsquo;s own &lt;code&gt;ExecStartPre&lt;/code&gt; writing the same key after your drop-in wins without touching your file, and a reload that reports success is not evidence that your value is the one in effect. The &lt;code&gt;-&lt;/code&gt; prefix, or &lt;code&gt;-e&lt;/code&gt;, suppresses even the loud errors, so a typo becomes invisible too.&lt;/p&gt;
&lt;h2 id="when-it-breaks"&gt;When it breaks&lt;/h2&gt;
&lt;p&gt;The classic failure is a config that reads correctly and does nothing. The file is on disk, &lt;code&gt;sysctl --system&lt;/code&gt; exits zero, and the key still holds its default, because it did not exist when the loader ran or because something wrote it afterwards. Diagnose by reading the key back rather than re-reading the file: &lt;code&gt;sysctl &amp;lt;key&amp;gt;&lt;/code&gt; shows the effective value, and comparing it against what the file claims is the whole test.&lt;/p&gt;
&lt;p&gt;The second failure is a container that believes it tuned the host. &lt;code&gt;net.*&lt;/code&gt; is per network namespace, so two containers can hold different values, but &lt;code&gt;vm.*&lt;/code&gt; and most &lt;code&gt;kernel.*&lt;/code&gt; keys are global; from a container they either change the whole machine or fail with EROFS when &lt;code&gt;/proc/sys&lt;/code&gt; is read-only. A &lt;code&gt;sysctl -w&lt;/code&gt; in an entrypoint is therefore not portable, and one that silently does nothing is worse than one that fails.&lt;/p&gt;
&lt;h2 id="check-it-quickly"&gt;Check it quickly&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sysctl -a | wc -l
sysctl net.ipv4.ip_forward net.core.somaxconn
sysctl -n net.ipv4.ip_forward
sysctl --system; echo &amp;#34;exit=$?&amp;#34;
grep -r . /etc/sysctl.d/ 2&amp;gt;/dev/null | head
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first line proves the table is enumerable, and the next two show the difference that matters when scripting: plain &lt;code&gt;sysctl&lt;/code&gt; prints &lt;code&gt;key: value&lt;/code&gt;, while &lt;code&gt;-n&lt;/code&gt; prints the bare value. &lt;code&gt;sysctl --system&lt;/code&gt; should exit zero, but that exit code proves very little, because a missing key is debug-level and non-fatal, so follow it with &lt;code&gt;sysctl &amp;lt;key&amp;gt;&lt;/code&gt; for every key you claim to manage. The &lt;code&gt;grep&lt;/code&gt; shows which files exist; remember that a same-named file in &lt;code&gt;/run&lt;/code&gt; or &lt;code&gt;/usr/lib&lt;/code&gt; may be masked, and that &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; is read by procps sysctl but not systemd-sysctl.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man8/sysctl.8.html"&gt;sysctl(8)&lt;/a&gt;, the runtime interface, the &amp;ndash;system file order, and the deprecated and verboten parameters, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man5/sysctl.conf.5.html"&gt;sysctl.conf(5)&lt;/a&gt;, the line format, the &amp;ldquo;-&amp;rdquo; prefix, and the note that systemd-sysctl does not read /etc/sysctl.conf, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man5/sysctl.d.5.html"&gt;sysctl.d(5)&lt;/a&gt;, drop-in precedence and the error classes that decide whether a failed line is even reported, checked 2026-09-16.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>