Firefox was crashing under heavy system load.
Analysis found that FF uses more memory than my system has available, causing the swap to thrash.
Trying earlyoom to kill tabs instead of crashing.
systemctl status earlyoom # is it running
journalctl -u earlyoom -f # watch it live
journalctl -u earlyoom | grep -i kill # what it has killed/etc/default/earlyoom
EARLYOOM_ARGS="-m 10,5 -s 20,10 -r 3600 --avoid ^(phpstorm|Xorg|cinnamon|cinnamon-sessio|cinnamon-launch|sshd|systemd|dockerd|containerd|mariadbd|mysqld|postgres|php-fpm|pipewire|pulseaudio|claude)$ --prefer ^(Isolated.Web.Co|Web.Content|Isolated.Servic|WebExtensions|Privileged.Cont|chrome|slack|ZoomWebviewHost|zoom)$"This setting in FF can also be helpful: browser.tabs.unloadOnLowMemory.
~/.mozilla/firefox/aii3qer6.default-release/user.js
https://support.mozilla.org/en-US/kb/unload-inactive-tabs-save-system-memory-firefox
From claude:
1. vm.max_map_count — this may be your actual Firefox crash
vm.max_map_count = 65530 ← current
firefox-bin: 26,427 maps ← 40.3% of the limit, in one process
Each process can hold a limited number of memory mappings. When a process hits the ceiling, mmap() fails and the app aborts — which presents exactly as "Firefox crashed," not as an OOM. Your parent process is already at 40% and this grows with tab count and uptime, and you run 4-day uptimes with 300 tabs.
Fedora, Arch and SteamOS all ship 1048576 now precisely because browsers and Electron apps hit the old limit. It's an accounting limit, not an allocation — raising it costs nothing.
2. vm.watermark_scale_factor — the freeze mechanism itself
vm.watermark_scale_factor = 10 ← 0.1% of RAM
This controls how early kswapd starts reclaiming. At 10 on a 30 GB machine, background reclaim doesn't begin until free memory is down to ~30 MB. Past that, every allocation goes into direct reclaim — the requesting process stalls synchronously while the kernel hunts for pages. That is the freeze. Raising it to 200 (2%) gives kswapd ~600 MB of runway to reclaim in the background instead of stalling your UI.
This is the single most underrated setting for desktop responsiveness under pressure, and it pairs with earlyoom rather than duplicating it: earlyoom kills, this prevents you reaching the point where killing is needed.
3. vm.swappiness = 60 → 10
The default is tuned for servers where anonymous memory is cold. On a 30 GB desktop it pushes application memory to NVMe swap while keeping page cache that's cheap to re-read. Lowering it prefers dropping cache over swapping your running apps.
4. Magic SysRq is missing its most useful bit
kernel.sysrq = 176 (= 128 + 32 + 16)
Bit 64 — "signalling of processes" — is not set, so Alt+SysRq+F (manual OOM kill) is disabled. That's your escape hatch when the machine is unresponsive: it kills the biggest memory hog without a hard reset. Setting 240 (176 + 64) enables it while keeping what you have.
5. zswap — compressed swap tier (bigger change)
zswap enabled: N
compressor: lzo (old default; zstd compresses far better)
zpool: zbud (stores 2 pages/page; zsmalloc is denser)
swap: /dev/nvme0n1p7, priority -2, no compression
zswap compresses pages in RAM before they reach disk swap — typically 3:1 with zstd, so much of your 30 GB swap workload would never touch the NVMe. I'd pick this over zram since your swap partition already exists. It needs a GRUB cmdline change and a reboot, unlike the others.
Suggested file
The first four are runtime-applyable, no reboot:
# /etc/sysctl.d/99-memory-tuning.conf
vm.max_map_count = 1048576
vm.watermark_scale_factor = 200
vm.swappiness = 10
kernel.sysrq = 240
Two I'd deliberately leave alone: vfs_cache_pressure (100 is right) and THP, already at madvise — always causes allocation stalls of its own.Level
Topics