持久化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 | 按声明重写 | 声明面 |
会真的「丢」的四种情形
- GC 回收:nix-collect-garbage 删掉 /nix/store 里没有 GC root 的路径。手动装的二进制如果只在脚本里写了 /nix/store/… 绝对路径,会被回收后失效。(本机 gc 是 weekly + --delete-older-than 30d。)
- 覆盖手改的声明文件:手改了 nix 声明过的 /etc 文件,下次激活按声明写回。
- 把二进制放进 nix 会重建的目录(少见):等于放进了声明面。
- 重装 / 换机:这才是手动安装真正「全部丢失」的场景——新机器上只有 configuration.nix/modules 声明的东西会重现,家目录之外的手动安装不在声明里,就没了。
本机实测证据
| 项 | 实测值 |
|---|---|
| system.etc.overlay | mutable(/etc 保持可写,本地非声明文件留得住) |
| nix.gc(定义在 modules/desktop/agent/hermes.nix:12) | automatic=true;dates=weekly;options="--delete-older-than 30d" |
| nix-gc.timer | NEXT 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