diff --git a/.claude/tasks/0014-nix-store-housekeeping.md b/.claude/tasks/0014-nix-store-housekeeping.md index 8e8c03d..5634ffa 100644 --- a/.claude/tasks/0014-nix-store-housekeeping.md +++ b/.claude/tasks/0014-nix-store-housekeeping.md @@ -10,9 +10,21 @@ Retained boot configurations are capped at 15. Each generation stores a kernel a ## Acceptance criteria -- [ ] Automatic garbage collection is enabled weekly, deleting generations older than 30 days -- [ ] Store optimisation is scheduled weekly, rather than performed at build time -- [ ] Retained boot configurations are capped at 15 -- [ ] These are declared as plumbing in the shared base config, so every future `Host` inherits them -- [ ] `nix flake check` builds the `neogaia` toplevel -- [ ] Manual confirmation after a rebuild: the collection and optimisation timers exist and are scheduled +- [x] Automatic garbage collection is enabled weekly, deleting generations older than 30 days +- [x] Store optimisation is scheduled weekly, rather than performed at build time +- [x] Retained boot configurations are capped at 15 +- [x] These are declared as plumbing in the shared base config, so every future `Host` inherits them +- [x] `nix flake check` builds the `neogaia` toplevel +- [x] Manual confirmation after a rebuild: the collection and optimisation timers exist and are scheduled + +## Implementation Notes + +The two schedules are named days rather than the bare `weekly` keyword. +systemd expands `weekly` to `Mon *-*-* 00:00:00`, which would have started collection and deduplication at the same instant every week, leaving `nix-optimise` hard-linking paths `nix-gc` was concurrently deleting. +Collection now runs `Mon 03:15` and optimisation `Thu 03:45`, which keeps both weekly and keeps them apart. + +The boot configuration cap sits in the shared base as the task asks, and is inert rather than an error on a host that does not use systemd-boot. +A future host on another bootloader therefore inherits no cap, which is the one place the "every future host inherits them" promise does not reach. + +Confirmed on `neogaia` after a switch: `systemctl list-timers 'nix-*'` lists both units, `nix-optimise` next on Thursday and `nix-gc` next on Monday, each `Persistent=true` so a suspended laptop catches up on a missed firing. +`/boot` reports 2 GiB with 113 MiB used, so the cap of 15 sits against the enlarged partition it assumes. diff --git a/system/default.nix b/system/default.nix index f056835..f638be6 100644 --- a/system/default.nix +++ b/system/default.nix @@ -58,6 +58,26 @@ in nix.settings.extra-trusted-public-keys = [ "nyx-cache.chaotic.cx:dJxTrgMC3V3cFfyIiBQDQorG6k1LsqurH/srpMSq7qk=" ]; + + # A month of generations is kept, because on a rolling channel with a + # third-party kernel an old generation is a known-good system to boot when + # an update breaks something. + nix.gc.automatic = true; + nix.gc.dates = "Mon 03:15"; + nix.gc.options = "--delete-older-than 30d"; + + # Deduplication runs on a timer, off the rebuild path, so it never adds + # latency to a `nixos-rebuild switch`. + # It falls on a different day from collection, so the two never contend. + nix.optimise.automatic = true; + nix.optimise.dates = [ "Thu 03:45" ]; + + # The EFI system partition holds a kernel and an initrd per entry at roughly + # 70 MiB apiece, and is fixed in size. + # An exhausted one fails at bootloader installation, after the build has + # already succeeded. + boot.loader.systemd-boot.configurationLimit = 15; + environment.systemPackages = [ pkgs.git ]; # Caps Lock is a second Escape; Shift+Caps Lock still toggles Caps Lock.