feat(memory): enable zram compressed-RAM swap by default (#76 slice)
All checks were successful
Check / eval (push) Successful in 4m1s
All checks were successful
Check / eval (push) Successful in 4m1s
First slice of BACKLOG #76 (hibernation + zram, split): zramSwap on by default in modules/nixos/oom.nix — the memory-pressure layer before earlyoom. zstd @ 50% RAM (nixpkgs defaults, explicit for reproducibility), priority 100 so day-to-day paging fills zram first and any disk swapfile stays reserved for #76's hibernate image (hibernation can't resume from volatile zram). Follows the earlyoom mkDefault precedent — no toggle. Adds checks.zram-swap runNixOSTest importing oom.nix, asserting the device is an active swap, uses [zstd], and carries priority 100. Verified: V2 checks.zram-swap PASS (/dev/zram0 in /proc/swaps, zstd, prio 100); V1 full distro toplevel builds; V0 flake check --no-build green. #76 remainder (disk swapfile default + resume + installer) still open. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -28,12 +28,18 @@ in [`docs/ROADMAP.md`](../docs/ROADMAP.md); map in
|
|||||||
|
|
||||||
## NEXT
|
## NEXT
|
||||||
|
|
||||||
### 76. Hibernation default + zram `[big]`
|
### 76. Hibernation default `[big]`
|
||||||
**Product intent (Bernardo 2026-07-10):** hibernation on by default;
|
**Product intent (Bernardo 2026-07-10):** hibernation on by default;
|
||||||
zram on alongside for live memory pressure. Disk swap ≥ RAM for the
|
zram on alongside for live memory pressure. Disk swap ≥ RAM for the
|
||||||
hibernate image; zram high-priority for day-to-day swap (not sufficient
|
hibernate image; zram high-priority for day-to-day swap (not sufficient
|
||||||
alone for resume).
|
alone for resume).
|
||||||
|
|
||||||
|
**✓ zram slice shipped (2026-07-10):** `zramSwap` on by default in
|
||||||
|
`modules/nixos/oom.nix` (zstd, 50% RAM, priority 100 so day-to-day paging
|
||||||
|
stays off the disk swap reserved for the hibernate image); `checks.zram-swap`
|
||||||
|
VM test asserts device/algorithm/priority (V2 pass). **Remainder below is the
|
||||||
|
hibernation half** — disk swapfile default + resume + installer.
|
||||||
|
|
||||||
**Reference layout (this machine — already hibernation-capable, no zram
|
**Reference layout (this machine — already hibernation-capable, no zram
|
||||||
yet):** LUKS (`crypto_LUKS` → mapper `crypted`) holds one BTRFS with
|
yet):** LUKS (`crypto_LUKS` → mapper `crypted`) holds one BTRFS with
|
||||||
`@swap` mounted at `/swap`; swapfile `/swap/swapfile` (here 16G);
|
`@swap` mounted at `/swap`; swapfile `/swap/swapfile` (here 16G);
|
||||||
@@ -47,12 +53,12 @@ Installer path already sketches this (`@swap` + resume_offset patch in
|
|||||||
|
|
||||||
**Scope (split before/while implementing — do not land as one mega-PR):**
|
**Scope (split before/while implementing — do not land as one mega-PR):**
|
||||||
|
|
||||||
1. **Design** — default `swapSize` (= RAM? round up?), zram size/priority
|
1. **Design** — default `swapSize` (= RAM? round up?), LUKS+systemd-initrd
|
||||||
vs disk prio, LUKS+systemd-initrd resume (already used), what when
|
resume (already used), what when install chose `swapSize=0`, migration
|
||||||
install chose `swapSize=0`, migration for existing machines.
|
for existing machines. (zram priority-vs-disk already settled + shipped.)
|
||||||
2. **Module** — NixOS: enable zram by default; ensure hibernation knobs
|
2. **Module** — NixOS: ensure hibernation knobs are coherent when swapfile
|
||||||
are coherent when swapfile exists (resume device/offset); Power menu
|
exists (resume device/offset); Power menu Hibernate already present —
|
||||||
Hibernate already present — must work when swap is configured.
|
must work when swap is configured. (zram already default-on in oom.nix.)
|
||||||
3. **Installer** — default new installs to hibernation-ready `@swap`
|
3. **Installer** — default new installs to hibernation-ready `@swap`
|
||||||
swapfile (encrypted with root); keep `0` = no swap opt-out; document
|
swapfile (encrypted with root); keep `0` = no swap opt-out; document
|
||||||
migration.
|
migration.
|
||||||
@@ -62,11 +68,9 @@ Installer path already sketches this (`@swap` + resume_offset patch in
|
|||||||
(`runNixOSTest` or install-test style) — encrypted root + BTRFS
|
(`runNixOSTest` or install-test style) — encrypted root + BTRFS
|
||||||
`@swap` swapfile + resume_offset; prove hibernate→resume path at
|
`@swap` swapfile + resume_offset; prove hibernate→resume path at
|
||||||
least as far as QEMU allows (fail the item if only “config
|
least as far as QEMU allows (fail the item if only “config
|
||||||
evaluates”). Also assert zram is active with expected priority
|
evaluates”).
|
||||||
relative to the swapfile.
|
|
||||||
- **V3:** real laptop (this LUKS+@swap layout): Hibernate → power off
|
- **V3:** real laptop (this LUKS+@swap layout): Hibernate → power off
|
||||||
→ resume session; queue HARDWARE-QUEUE. zram under load is
|
→ resume session; queue HARDWARE-QUEUE.
|
||||||
secondary.
|
|
||||||
|
|
||||||
**Out of scope:** TLP; formatter. Prefer boring NixOS knobs; extend the
|
**Out of scope:** TLP; formatter. Prefer boring NixOS knobs; extend the
|
||||||
existing installer swapfile story rather than inventing a second layout.
|
existing installer swapfile story rather than inventing a second layout.
|
||||||
|
|||||||
@@ -17,6 +17,22 @@ Template:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-07-10 — #76 zram slice: compressed-RAM swap default
|
||||||
|
- **Task:** BACKLOG #76 (`[big]`) — split; took the low-risk zram-default slice.
|
||||||
|
- **Did:** `zramSwap` on by default in `modules/nixos/oom.nix` (zstd, 50% RAM,
|
||||||
|
priority 100) as the memory-pressure layer before earlyoom, following the
|
||||||
|
earlyoom mkDefault precedent (no toggle). High priority keeps day-to-day
|
||||||
|
paging in zram so a disk swapfile stays reserved for #76's hibernate image.
|
||||||
|
Added `checks.zram-swap` runNixOSTest (imports oom.nix). Split #76 in
|
||||||
|
BACKLOG → remainder is the hibernation half (swapfile/resume/installer).
|
||||||
|
- **Verified:** **V2** — `nix build .#checks…zram-swap` PASS: /dev/zram0 active
|
||||||
|
in /proc/swaps, `[zstd]` comp_algorithm, priority 100. **V1** full distro
|
||||||
|
toplevel builds. **V0** flake check --no-build green.
|
||||||
|
- **Pending:** hibernation half of #76 still open (needs hibernate→resume VM
|
||||||
|
test + V3 laptop). No V3 for zram (under-load tuning is secondary).
|
||||||
|
- **Next suggestion:** #76 hibernation half — design default swapSize + the
|
||||||
|
resume/installer wiring; it's the boot-critical, V2-hibernate + V3 part.
|
||||||
|
|
||||||
## 2026-07-10 — #41 hardware-confirmed: OpenFile portal floats (V3 close)
|
## 2026-07-10 — #41 hardware-confirmed: OpenFile portal floats (V3 close)
|
||||||
- **Task:** BACKLOG #41 — final on-hardware confirmation (Bernardo reported PASS).
|
- **Task:** BACKLOG #41 — final on-hardware confirmation (Bernardo reported PASS).
|
||||||
- **Did:** #41 already out of NEXT + portal rule shipped
|
- **Did:** #41 already out of NEXT + portal rule shipped
|
||||||
|
|||||||
25
flake.nix
25
flake.nix
@@ -659,6 +659,31 @@
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# zram compressed-RAM swap (oom.nix, BACKLOG #76 slice): enabled
|
||||||
|
# by default as the memory-pressure layer *before* earlyoom. Prove
|
||||||
|
# the device comes up as an active swap, uses zstd, and carries the
|
||||||
|
# high priority that keeps day-to-day paging off any disk swapfile
|
||||||
|
# (which #76 reserves for the hibernate image). Imports oom.nix
|
||||||
|
# directly so the check tracks the module, not a hand-copy.
|
||||||
|
zram-swap = pkgs.testers.runNixOSTest {
|
||||||
|
name = "nomarchy-zram-swap";
|
||||||
|
nodes.machine = { ... }: {
|
||||||
|
imports = [ ./modules/nixos/oom.nix ];
|
||||||
|
virtualisation.memorySize = 1024;
|
||||||
|
};
|
||||||
|
testScript = ''
|
||||||
|
# zram-generator brings the device up as swap shortly after boot.
|
||||||
|
machine.wait_until_succeeds("grep -q /dev/zram0 /proc/swaps", timeout=60)
|
||||||
|
# Configured compression algorithm is the selected one ([zstd]).
|
||||||
|
machine.succeed("grep -q '\\[zstd\\]' /sys/block/zram0/comp_algorithm")
|
||||||
|
# High priority (100) so the kernel fills zram before any disk
|
||||||
|
# swap — the ordering #76's hibernate image depends on.
|
||||||
|
machine.succeed(
|
||||||
|
"awk '$1==\"/dev/zram0\" && $5==100 {ok=1} END{exit !ok}' /proc/swaps"
|
||||||
|
)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
# Snapshot GUI canary: btrfs-assistant crashes in libbtrfsutil's
|
# Snapshot GUI canary: btrfs-assistant crashes in libbtrfsutil's
|
||||||
# UNPRIVILEGED subvolume iteration (btrfs-progs 6.17.1, fixed
|
# UNPRIVILEGED subvolume iteration (btrfs-progs 6.17.1, fixed
|
||||||
# upstream after 6.17.1) but runs fine as root — which is how the
|
# upstream after 6.17.1) but runs fine as root — which is how the
|
||||||
|
|||||||
@@ -14,9 +14,28 @@
|
|||||||
# the build step, keep the session". nixpkgs default-enables oomd in an
|
# the build step, keep the session". nixpkgs default-enables oomd in an
|
||||||
# inert state (no slices monitored); it's disabled outright below so
|
# inert state (no slices monitored); it's disabled outright below so
|
||||||
# there is exactly one owner of the OOM story.
|
# 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, ... }:
|
{ 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 = {
|
services.earlyoom = {
|
||||||
enable = lib.mkDefault true;
|
enable = lib.mkDefault true;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user