# Memory-pressure protection: keep the desktop alive when RAM runs out. # # A workstation that compiles from source WILL exhaust memory eventually — # a big `nix build`, a runaway eval, a browser tab. The kernel's own OOM # killer acts only after the system has thrashed itself unresponsive # (often minutes of frozen desktop) and then picks by badness score, # which can land on the compositor. # # earlyoom over systemd-oomd — a deliberate choice: oomd kills whole # cgroups, and a Hyprland session runs as ONE scope (nothing spawns # per-app systemd scopes here, unlike GNOME), so under pressure oomd # would take out the entire desktop to save it. earlyoom kills a single # process (highest oom_score ≈ the hog) BEFORE the thrash point — "kill # the build step, keep the session". nixpkgs default-enables oomd in an # inert state (no slices monitored); it's disabled outright below so # there is exactly one owner of the OOM story. # # zram sits one layer earlier: a compressed RAM swap that absorbs # day-to-day memory pressure (a browser's idle tabs, a big eval's cold # pages) before it ever reaches the earlyoom threshold — more headroom # on the same RAM, no disk. High swap priority so the kernel fills zram # first; any real disk swapfile stays reserved for the hibernate image # (BACKLOG #76's other half — hibernation can't resume from volatile # zram, so day-to-day paging must not consume the disk swap). zstd @ 50% # RAM are the nixpkgs defaults, kept explicit for reproducibility. { lib, ... }: { zramSwap = { enable = lib.mkDefault true; algorithm = lib.mkDefault "zstd"; memoryPercent = lib.mkDefault 50; # Beats any disk swapfile's auto-assigned (negative) priority, so # day-to-day paging lands in zram and the disk swap is left for # hibernation. See the header note and BACKLOG #76. priority = lib.mkDefault 100; }; services.earlyoom = { enable = lib.mkDefault true; # Desktop toast when something is killed (relayed via # systembus-notify), so a vanished build/tab is explained rather # than mysterious. enableNotifications = lib.mkDefault true; # Never pick the session plumbing: losing the compositor or the lock # screen IS the outage this module exists to prevent (and killing a # Wayland session-lock client trips its go-to-a-tty failsafe). No # --prefer tuning: highest-memory selection already targets the hog. # Matched unanchored — NixOS wrappers rename comm to ".foo-wrapped". extraArgs = lib.mkDefault [ "--avoid" "(Hyprland|hyprlock|greetd|waybar|pipewire|wireplumber|Xwayland|nix-daemon|systemd)" ]; }; # One owner (see header): oomd ships default-on but inert; make the # earlyoom choice explicit and total. systemd.oomd.enable = lib.mkDefault false; }