NixOS 更新前自动快照方案
NixOS 更新前自动快照方案
本文记录 HX90 的自动更新保护方案。目标是在每次正式更新前,自动保存系统、 用户目录和 NixOS generation 的对应关系;出现问题时,可以先查看记录,再选择 回滚系统配置、恢复 Btrfs 快照,或两者一起处理。
本文记录已经实现的自动化流程以及恢复边界。恢复 / 和 /home 仍然不会自动执行,
需要根据记录人工确认。
目标
以后正式更新统一使用:
nixos-update
它完成以下工作:
检查当前系统和仓库状态
↓
记录当前 NixOS generation、flake revision 和时间
↓
创建 / 的 Btrfs/Snapper 快照
创建 /home 的 Btrfs/Snapper 快照
↓
更新 Nix flake 输入
更新 Omarchy git 插件
↓
构建 NixOS 配置
↓
切换到新的 NixOS generation
↓
记录成功、失败或中断状态
↓
按 Snapper 数量策略清理过期快照
如果更新失败,快照记录仍然应该保留,方便定位和恢复。
当前机器已确认的事实
HX90 的布局是:
/dev/nvme0n1p2 Btrfs
├── / 根文件系统(顶层 Btrfs volume)
├── /home 独立 Btrfs 子卷
└── /nix 独立 Btrfs 子卷
/dev/nvme0n1p3 独立 swap 分区
当前已有:
- NixOS generation,可使用
nixos-rebuild switch --rollback回滚系统配置; /、/home和/nix的 Btrfs 子卷;- zram + 磁盘 swap;
btrfs-progs;- 本地桌面的 NixOS 更新入口。
当前已实现:
- Snapper 的
root和home配置; nixos-update自动更新前快照;/var/lib/nixos-update/history/更新记录;nixos-update list和nixos-update show <transaction-id>查看命令。
恢复命令仍未自动化,避免误覆盖故障发生后新产生的文件。
三种回滚机制的职责
NixOS generation
nixos-rebuild list-generations
sudo nixos-rebuild switch --rollback
负责回滚:
- NixOS 配置;
- 系统软件包;
- 内核;
- systemd system service;
- NixOS 生成的
/etc内容。
不负责回滚:
/home中普通文件;- 浏览器数据;
- SSH key;
- 数据库和容器卷;
- 不属于 NixOS/Home Manager 的运行时状态。
/ 的 Btrfs/Snapper 快照
负责保存根文件系统某一时刻的状态,包括非 Nix 生成的系统文件和运行时数据。
它不能替代 NixOS generation。两者需要配对记录。
/home 的 Btrfs/Snapper 快照
负责保存用户目录,包括:
- chezmoi 配置和源仓库;
- 浏览器数据;
- SSH 配置;
- 用户文档;
- 用户级 systemd 状态;
- 不由 Home Manager 管理的用户文件。
/nix 通常不纳入更新前快照:Nix store 是不可变内容寻址存储,旧 generation
需要的 store path 会继续保留,直到执行垃圾回收。是否备份 /nix 是独立的备份
策略,不放入每次更新前快照。
Snapper 配置
系统已声明两个 Snapper 配置:
root → /
home → /home
两个快照会使用同一个更新事务 ID,例如:
update-2026-09-05T153000-generation-6
这样可以在记录中把两个快照配成一组:
transaction = update-2026-09-05T153000-generation-6
root_snapshot = 42
home_snapshot = 17
generation_before = 6
快照默认使用只读模式或等价的保护策略,防止普通应用意外修改历史快照。
nixos-update 行为
完整执行顺序
- 定位
NIXOS_CONFIG(默认~/nixos-config)和主机目标。 - 检查工具和未提交改动,按用户选择保留改动或临时 stash。
- 持有 sleep/idle inhibitor,创建事务记录与 root/home 快照。
- 在临时复制中运行
nix flake update,更新本仓库独立输入。 - 使用
--impure构建候选配置;失败时停止,保留记录和快照。 - 更新用户 Omarchy 插件,将候选 lock 提升到源仓库,再切换候选系统。
- 记录结果并恢复临时 stash;失败时恢复原 flake 文件。
nixos-update check 只在临时副本更新输入并显示 lock diff,不修改源仓库或系统。
--yes 接受更新流程,不跳过构建。非交互环境遇到脏仓库须明确使用
--allow-dirty,否则停止。此命令不查询 Nixarchy releases。
更新前检查
命令启动后先检查:
- 当前是否是 NixOS;
- 主机名是否能匹配 flake 配置;
/和/home是否为支持快照的 Btrfs 子卷;- Snapper 配置是否存在;
~/nixos-config是否有未提交改动;- 是否已经有另一个更新任务运行;
- 当前磁盘剩余空间是否足够创建快照元数据;
- 当前用户是否有可用的 sudo 权限。
发现配置仓库有未提交改动时,默认应停止,而不是把不清楚的状态标记为可恢复
基线。需要强制更新时,应提供明确的 --allow-dirty 选项并记录原因。
快照和元数据
快照前记录:
主机名
开始时间
当前 NixOS generation
当前运行 kernel
flake 当前 Git revision
flake.lock 的状态
root 快照编号
home 快照编号
记录位置:
/var/lib/nixos-update/history/
记录由 root 创建和维护,每个事务一份文本文件,方便 SSH 查询:
nixos-update list
nixos-update show <transaction-id>
journalctl -t nixos-update
更新 Flake
更新输入、构建与切换使用临时候选目录,不更新 Nixarchy 版本。
Labwc-plus 使用 flake.lock 中锁定的源码输入,普通构建不需要本机 checkout。
只有加载 NIXOS_CONFIG_LOCAL 中的忽略配置时才使用 --impure。
构建失败不切换系统,也不自动恢复 Btrfs 快照。
桌面接管清单见 nixarchy-removal.md。
Omarchy 插件更新
当前 Omarchy 4.0.1 提供:
omarchy plugin update [id] [--yes]
不带插件 ID 时,更新所有 git-managed 插件:
omarchy plugin update --yes
这一步已纳入 nixos-update。插件更新会修改用户目录,所以必须放在 /home
快照创建之后。插件自身如果遇到本地改动或验证失败,应保留失败状态并停止后续
系统切换,不能静默覆盖用户修改。
当前 ~/.config/omarchy/plugins.list 由 chezmoi 管理,但插件 checkout 的更新
动作由 Omarchy 命令负责。两者职责不同:
chezmoi
└── 记录插件清单和启用关系
omarchy plugin update
└── 更新已安装插件的 git checkout
防止自动睡眠
nixos-update 会使用 systemd-inhibit 锁住 sleep 和 idle,防止 SSH 发起的
长时间构建被桌面电源策略中断。HX90 平时的空闲自动睡眠也已禁用。
为什么不拦截所有 nixos-rebuild
NixOS 没有一个可以安全拦截所有任意 nixos-rebuild switch 调用的统一 pre-hook。
因此自动快照只对以下入口保证:
nixos-update
直接执行:
sudo nixos-rebuild switch --flake .#hx90
会绕过更新前快照流程。该命令仍可用于紧急修复、回滚或开发调试,但正式更新应
统一使用 nixos-update。
omarchy update 已转发到 nixos-update,使用相同的快照流程。
本地桌面菜单也保留 NixOS Update (Snapshot) 入口。
恢复流程设计
恢复不是默认自动执行的,因为恢复 / 或 /home 可能覆盖用户在故障后的新文件。
当前实现提供查看记录和快照的命令;实际恢复仍按本文的离线流程人工执行。
先判断问题类型
桌面、内核、systemd、Nix 包异常
→ 先尝试 NixOS generation 回滚
用户文件、浏览器数据、配置文件异常
→ 根据事务记录查看 /home 快照
系统文件和用户目录都需要回到更新前
→ 回滚 generation,并从同一个事务 ID 恢复 root + home 快照
查看记录
nixos-update list
nixos-update show <transaction-id>
sudo snapper -c root list
sudo snapper -c home list
本次更新的事务记录示例:
nixos-update show update-20260905T151243-generation-8
记录中的 root_snapshot、home_snapshot 和 generation_before 是一组,
恢复时应优先使用同一个事务中的编号,不要只凭快照编号猜测对应关系。
查看快照内容
Snapper 使用 0 表示当前正在使用的文件系统。比较更新前快照 1 和当前状态:
sudo snapper -c root status 1..0
sudo snapper -c root diff 1..0
sudo snapper -c home status 1..0
sudo snapper -c home diff 1..0
status 只列出新增、删除和修改的路径,diff 会显示文件内容差异。快照也可以
临时挂载后以普通文件的方式查看:
sudo snapper -c home mount 1
findmnt -t btrfs | grep snapper
sudo snapper -c home umount 1
NixOS generation 回滚
sudo nixos-rebuild list-generations
sudo nixos-rebuild switch --rollback
无法进入桌面时,从 systemd-boot 选择旧 generation。
Btrfs 快照恢复
当前提供查看命令和单文件恢复命令;恢复整个 / 和 /home 时不自动覆盖当前文件。
如果只是误删或误改了一个用户文件,可以先查看差异,再只恢复指定路径:
sudo snapper -c home undochange 1..0 -- /
也可以指定一个目录,但这会覆盖目录下对应的当前文件,执行前应确认路径范围:
sudo snapper -c home undochange 1..0 -- /
undochange 会直接修改当前文件系统,不能当作只读查看命令。重要文件应先复制
一份到其他位置。
推荐从 NixOS 安装介质或另一套可启动系统执行恢复,在离线状态下:
- 确认事务 ID;
- 确认 root 和 home 快照编号属于同一事务;
- 备份当前需要保留的新文件;
- 恢复
/; - 恢复
/home; - 重启并选择对应的 NixOS generation;
- 检查 SSH、网络、桌面和用户数据。
不要在尚未确认方案时直接执行:
sudo snapper -c root rollback 1
rollback 会创建新的读写快照并改变 Btrfs 默认启动子卷。它只处理对应的 Snapper
配置,不能自动同时恢复 /home,因此不等价于“root + home 一键恢复”。完整恢复
需要先备份故障发生后新增的文件,再分别处理 root 和 home,并选择匹配的旧 NixOS
generation 启动。
保留和清理策略
自动快照必须自动清理,否则会持续占用 Btrfs 空间。当前策略为:
最近 10 次更新事务:保留 root + home
最近 5 次重要事务:长期保留
普通快照超过 30 天:自动清理
用户手动标记的重要快照:不自动清理
当前 root 和 home 使用各自的 Snapper 数量清理策略。事务记录仍然会保留两者的 对应关系,但当前清理器不保证两个配置严格成对删除;如果某个事务必须长期保留, 应同时把 root 和 home 快照标记为重要,或暂时不要运行清理。后续如有需要,再增加 按事务 ID 成对清理的辅助逻辑。
快照只保护同一块磁盘上的快速回滚,不防止硬盘损坏。重要数据仍需使用外部磁盘、
NAS、btrfs send/receive、restic 或 borg 做异地/离线备份。
实施状态与后续
以下基础功能已经实施并验证:
- 安装并声明 Snapper;
- 建立
root和home配置; - 实现
nixos-update list/show; - 接入 flake 更新、插件更新、构建和 switch;
- 加入快照清理策略。
仍待后续验证或实现:
- 从 Live 环境实际演练
/和/home恢复; - 根据演练结果决定是否提供恢复辅助命令;
- 评估按事务 ID 成对清理 root/home 快照的辅助逻辑。
在恢复流程经过实际演练前,不会把自动恢复加入更新命令。