nix:持久化和运行时的关系

持久化rebuild+用户手动安装的一些软件,下次rebuild这些软件会全部丢失,还是nix未定义的都会保留,仅被nix定义的会被rebuild回去

结论

是后者:rebuild 只重写「nix 声明过的那一层」,nix 没定义的不会被动。拆开说:

  • 被 nix 定义的(configuration.nix 里的包、模块生成的 /etc 文件、systemd 单元、内核/驱动/固件)→ 每次 nixos-rebuild 按声明重写;手改的会被覆盖回去。
  • nix 没定义的(家目录里的软件、容器、VM 磁盘、/var 里的数据)→ rebuild 不碰,保留。
  • 但「nix 未定义的一律保留」要加一个限定:落在 /nix/store 里、又没有任何引用(GC root)的东西,会被 nix-collect-garbage 回收。手动装的软件只要文件在家目录(不在 store),就与 GC 无关。

模型:持久化面 vs 运行时面

  • 持久化面 = 你「说出来」的东西:/etc/nixos 里的 .nix 声明,以及由它生成到 /nix/store、/etc、systemd 的结果。rebuild 就是把这面对账一遍。
  • 运行时面 = 你「做出来」的东西:家目录里装的软件与配置、容器、VM、/var 数据、命令式 nix profile。rebuild 原样不动。
  • 一句话:声明式的随声明复现;其余的在原地活着,直到你换机或 GC 动手。

各类「手动安装」的落点与 rebuild 后的命运

装的东西实际落点下次 rebuild说明
configuration.nix 的 environment.systemPackages/nix/store + /run/current-system/sw按新声明重写这是 nix 定义面,声明里去掉就真没了
nix-env -i / nix profile install(命令式)per-user profile(/nix/var/nix/profiles/per-user/<用户>)不被 nixos-rebuild 动nixos-rebuild 只管系统 profile;命令式装的要靠 GC root 保命
手动 nix build 的产物 / 临时软链/nix/store可能被 GC 删store 里无引用即回收
手改「nix 声明过」的 /etc 文件/etc被 nix 版覆盖声明面,激活时按声明写回
手放进 /etc 的「nix 不管」的文件(如自写 systemd unit)/etc保留非声明面
pip --user / npm -g / cargo / go / conda~/.local、~/.cargo、~/go、conda 目录保留家目录
Flatpak~/.local/share/flatpak保留家目录
AppImage / 下载的二进制 / 解压式软件~/.local/opt、~/Applications 等保留家目录(本机例:~/.local/opt/trilium-cn-0.63.7)
官方 install.sh 装的 Hermes~/.local/bin + ~/.hermes保留家目录(本机例:~/.local/bin/hermes)
Docker 镜像 / 容器 / 卷/var/lib/docker保留除非某个 nix 模块重建时把它重置了
libvirt / KVM 虚拟机磁盘/var/lib/libvirt 或自定义路径保留非声明面
内核 / 驱动 / 固件nix store按声明重写声明面

会真的「丢」的四种情形

  1. GC 回收:nix-collect-garbage 删掉 /nix/store 里没有 GC root 的路径。手动装的二进制如果只在脚本里写了 /nix/store/… 绝对路径,会被回收后失效。(本机 gc 是 weekly + --delete-older-than 30d。)
  2. 覆盖手改的声明文件:手改了 nix 声明过的 /etc 文件,下次激活按声明写回。
  3. 把二进制放进 nix 会重建的目录(少见):等于放进了声明面。
  4. 重装 / 换机:这才是手动安装真正「全部丢失」的场景——新机器上只有 configuration.nix/modules 声明的东西会重现,家目录之外的手动安装不在声明里,就没了。

本机实测证据

项实测值
system.etc.overlaymutable(/etc 保持可写,本地非声明文件留得住)
nix.gc(定义在 modules/desktop/agent/hermes.nix:12)automatic=true;dates=weekly;options="--delete-older-than 30d"
nix-gc.timerNEXT 2026-10-19 00:00:00 / LAST 2026-10-12 00:00:14(每周一)
命令式 nix 安装/nix/var/nix/profiles/per-user/ 下只有 root,cat 无 per-user profile
家目录手动装面(抽样)~/.local/opt/trilium-cn-0.63.7、~/.local/bin/{hermes,hermes-agent,hermes-acp,kde-font-zoom,python3.13}
系统 profile 世代34 个;current 指向 2026-10-15_14-44-52-kwin-hotkey-probe2
home-manager未使用(配置里没有)

实践建议

  • 要「rebuild 后一定在」的东西 → 写进 nixos 声明(modules/<域>/<topic>.nix),别手动装。
  • 手动装的(AppImage、官方 install.sh、flatpak)→ 家目录放着就安全,但要记档(本项目做法:modules/desktop/flatapk软件.txt 之类),否则换机时续不上。
  • 别用命令式 nix-env / nix profile 长期维护——它不进声明、rebuild 不重建、还可能被 GC 干掉(本机目前没有,保持这样)。
  • 手动改 /etc 的东西,若是 nix 也声明的项(如 kwinrc、mimeapps.list)→ 改到 nix 里,否则下次被覆盖。

复核命令

# 某物在不在 nix 声明面
nixos-option environment.systemPackages | grep -i <名称>
# 看某个 store 路径有没有 GC root(没有就会被回收)
nix-store -q --roots /nix/store/<hash>-<name>
# GC 会删什么(先 dry-run,不实删)
nix-collect-garbage --dry-run
# /etc 里哪些是 nix 声明的
nixos-option environment.etc
# 当前系统世代 / 历史世代
readlink -f /run/current-system ; nix-env --list-generations -p /nix/var/nix/profiles/system | tail