Updating this flake safely
Read this before running just up, just upp, just up-nix, or just override-pkgs, and before
deploying the result to any host.
Core rules
- Update and deploy are two steps. Never fold "bump the lock file" and "switch every host" into one blind action.
- Validate before touching a host. An update is only a candidate until
just testis green and the affected hosts build locally. - One reviewable commit per update. The convention here is a
flake.lock: Updatecommit for the bump; a config change gets its own commit or PR. - Keep a way back. Anything that reaches a host needs a rollback path: the previous generation,
bootmode,just darwin-rollback, orgit revert. - Security first. An update re-opens the supply chain. Re-audit every input whose revision moved before you deploy it.
- Pin third-party inputs. A third-party input — not one of the user's own
ryan4yin/*repositories — MUST name a version tag or a fixed commit, not a branch. Official channel upstreams that publish no release (nixpkgs'nixos-unstable/master) are the exception. When only a branch carries a needed third-party change, keep the resolved revision in the lock, record why next to the declaration, and re-audit each update.
1. Pre-flight
git statusis clean, and you are onmainor a fresh branch for the work. If you are on an unrelated feature branch, stop and ask for the right branch before bumping.just testis green before the update, so you have a baseline to compare against. It and every eval fetch the privatemysecretsinput over SSH; without that access they hang instead of failing.- Check WORKAROUNDS.md for the inputs and recipes you are about to use; an active row that names one of them replaces its command here.
- Enough disk for the new closures:
df -h /nix/store. Broad nixpkgs bumps pull a lot. - Know what will move and choose the narrowest recipe that does the job.
2. Run the update
- Everything:
just up— updates and commits the lock file automatically; audit before pushing - One input:
just upp <input>— same, for a single input, e.g.just upp catppuccin; audit before pushing - The nixpkgs family:
just up-nix— updatesnixpkgs-stable/-master/-darwin/-patched; notnixpkgs - Pin
nixpkgs:just override-pkgs <hash>— pinnixpkgsto a known-good Hydra commit - Update but keep it dirty:
nix flake update <input>— leaves the change uncommitted so you can inspect it first - Nix itself (macOS):
just nix-upgrade—determinate-nixd upgrade; macOS only
After an update, read git diff flake.lock: it lists exactly which inputs moved and by how many
commits. Because just up and just upp commit automatically, either use
nix flake update <input> while auditing or amend the generated lock commit after the audit so its
message records the conclusion.
3. Audit the change
Treat an update as a supply-chain event.
-
From
git diff flake.lock, list every input whoselocked.revchanged. -
Find the inputs that contribute modules this repo imports:
bashgrep -rn "homeModules\.\|nixosModules\.\|darwinModules\." --include='*.nix' . -
For each of those inputs, read what changed between the old and new revision in the module files we actually import. Look for:
- restricted
nix.settings(substituters,extra-substituters,trusted-public-keys,trusted-users) — at the Home Manager layer these are ignored for an untrusted user and only warn, but at the system layer they are a root-equivalent trust boundary; environment.etc/home.fileentries with amodeoruserthat widens access;system.activationScripts,systemd.servicesrunning as root, sudo/polkit rules;lib.mkForceoverrides that silently replace existing settings;- eval-time network access or import-from-derivation.
- restricted
-
For nixpkgs-class inputs, skim the lock diff and expect broken packages and eval deprecation warnings.
-
Write the audit conclusion into the update commit message. If an automatic recipe already made the commit, amend it only after reviewing the diff; do not push the unaudited commit first.
4. Validate before deploying
Run these before any host is touched:
just test— fails (non-zero) unless the suite returnstrue.just eval-host <host>— fast, evaluation only.just build-host <host>— builds the full system closure and catches broken packages or build failures that eval misses.just build-microvm <guest>— same, for a MicroVM guest.just test-vm— the runtime security-VM check (needs/dev/kvm);just testdoes not run it.nix flake check— broader checks when the change touches shared code.
Cover every host you are about to deploy, and prefer building the closure over trusting a green eval.
Preview what the machine you are on will change before it is deployed:
bashnix store diff-closures /run/current-system '.#nixosConfigurations.<host>.config.system.build.toplevel'
It lists every package whose version or size moves; an empty result means nothing changes. Read it for unexpected removals, major-version jumps, and kernel or systemd changes that need a reboot.
5. Deploy in stages
Modes are switch (take effect now, the default) and boot (only the next boot). Add debug for
verbose output. Anything else is rejected.
The user runs these for the machine you are on:
- Current desktop:
just niri [mode] [verbosity] - Other local NixOS host:
just local [mode] [verbosity] - macOS:
just local [debug](build then switch; macOS has no switch/boot split)
The rest authenticate over SSH as root on the target, so they run non-interactively — but they still change remote state. Confirm the target instead of trusting the recipe's default, then state it back to the user and get authorization for that host:
bashhostname # which machine you are on git branch --show-current git remote -v getent hosts <host> # the address the tag will connect to ssh root@<host> hostname # the host that actually answers
- Remote servers:
just shoryu [mode],just shushou,just youko,just ruby,just kana - All VM hosts at once:
just lab [mode]; any Colmena tag:just col <tag> [mode] - k3s test nodes:
just k3s-test [mode] - MicroVM guest:
just microvm-deploy <guest> <physical-host>installs the runner on the physical host and restarts the guest unit there (WA-026); switch that host afterwards, or the next activation reverts the guest to its own runner. Deploy guests serially and check each one before moving on.
Use boot plus a deliberate reboot for anything that can drop networking mid-flight: the VM hosts
with the br0 bridge, and broad nixpkgs bumps. See
hosts/README.md. Pause Flux sync on the k3s cluster
before changing a node's networking, and resume it once the node is back.
6. Verify after deploying
systemctl --failedandjust list-failedfor failed units.journalctl -b -p errfor boot-time errors.- The user-visible surface: network, the desktop session, and the specific service you changed.
- Re-run
just testso a dirty tree cannot hide a regression.
7. Roll back
- NixOS:
just historylists system generations; pick the previous one in the bootloader menu, or re-deploy a reverted tree. - macOS:
just darwin-rollback(darwin-rebuild --rollback). - Remote (Colmena):
git revert <sha>and re-apply, or apply withbootand reboot. - Update failed but never deployed: drop the bump's own uncommitted lock change with
git checkout -- flake.lock, then reproduce. If the tree holds other uncommitted work, ask before discarding anything. - Isolate a bad bump by moving one input at a time with
nix flake update <input>, and pin a known-good nixpkgs withjust override-pkgs <hash>while the breakage is fixed upstream.
8. Clean up only after it is stable
just gc (older than 7 days) and just clean (wipes profile history) delete the generations you
would roll back to, so run them last, once the update has proven stable. just gcroot only lists GC
roots. The full list of hazardous recipes is in the Command Hazards section of
AGENTS.md.


