Day 4:版本管理 — Generation 與 build-vm 的安全網
深入理解 NixOS 的 Generation 機制與版本管理,學會使用 build-vm 在虛擬機中安全測試配置變更,避免系統損壞風險。
Day 4:版本管理 — Generation 與 build-vm 的安全網
🗂️ 系列:NixOS 30 天學習之旅 📦 階段:第一階段 — 基礎與生存守則 (Day 1 – Day 7) 🎯 階段核心目標:理解聲明式 (Declarative) 配置與不可變性
前言:為什麼版本管理對 NixOS 特別重要?
在傳統的 Linux 發行版上,每次安裝、更新或移除套件,都是對系統進行「就地修改 (in-place mutation)」。一旦改壞了什麼,想回到上一個正常的狀態,往往得靠記憶、靠備份、靠運氣。
NixOS 從根本上解決了這個問題。
還記得 Day 2 提過的聲明式配置嗎?每一次你執行 nixos-rebuild switch,NixOS 就會根據你的 configuration.nix 產生一個全新的系統 generation(世代)。這些 generation 不會彼此覆蓋,而是共存在系統中。你可以隨時列出、切換、甚至刪除任何一個 generation。
更棒的是,NixOS 還提供了 nixos-rebuild build-vm 這個指令,讓你在修改配置之前,先用虛擬機 (VM) 跑一遍——完全不動到你的主機。
今天,我們就來學會這張「安全網」。
Generation 的概念
每次 rebuild,就是一個新世代
在 NixOS 的世界觀裡,系統狀態不是「現在長什麼樣子」,而是「你告訴它應該長什麼樣子」。每次你執行 nixos-rebuild switch,系統就會:
- 讀取
/etc/nixos/configuration.nix(及其 imports) - 根據配置計算出完整的系統 closure
- 建構所有需要的套件與設定檔
- 產生一個新的 generation,並將它設為「目前啟用的版本」
- 更新 bootloader,把這個 generation 加入開機選單
這就像是 Git 的 commit:每個 generation 都是系統在某個時間點的完整快照 (snapshot),而且不可變 (immutable)。
Generation 的存放位置
NixOS 的 generation 資訊存放在 /nix/var/nix/profiles/ 目錄下:
ls -la /nix/var/nix/profiles/system-*
你會看到類似這樣的輸出:
system-1-link -> /nix/store/abc123...-nixos-system-nixos-24.05
system-2-link -> /nix/store/def456...-nixos-system-nixos-24.05
system-3-link -> /nix/store/ghi789...-nixos-system-nixos-24.05
每個 system-N-link 就是一個 generation,指向 /nix/store 裡的一個不可變路徑。
列出與管理 Generations
列出所有 generation
最直覺的方式是使用 nix-env 搭配 --list-generations 旗標:
sudo nix-env --list-generations --profile /nix/var/nix/profiles/system
輸出大概長這樣:
1 2024-01-15 10:30:00
2 2024-01-16 14:22:33
3 2024-01-20 09:15:47 (current)
標示 (current) 的就是你目前正在使用的 generation。
查看兩個 generation 之間的差異
想知道上次 rebuild 到底改了什麼嗎?可以用 nix profile diff-closures 來比較:
nix profile diff-closures --profile /nix/var/nix/profiles/system
這個指令會列出每個 generation 之間新增、移除、升級的套件,非常適合用來追蹤變更歷史。
刪除舊的 generation
隨著時間推移,generation 會越來越多,佔用 disk 空間。你可以用以下指令清理:
# 刪除 30 天前的 generation
sudo nix-collect-garbage --delete-older-than 30d
或是更精確地指定要保留哪些:
# 只保留最近 5 個 generation
sudo nix-env --profile /nix/var/nix/profiles/system --delete-generations +5
⚠️ 注意:刪除 generation 之後,你就無法再 rollback 到那些版本了。建議至少保留最近 2–3 個 generation 作為安全緩衝。
刪除 generation 之後,記得執行 garbage collection 來真正釋放 disk 空間:
sudo nix-collect-garbage
nixos-rebuild build-vm 實戰
為什麼需要 build-vm?
想像一下這個情境:你打算在 configuration.nix 裡啟用一個新的 service,比如 Nginx。你不太確定設定對不對,也擔心會不會影響到目前正常運作的系統。
傳統做法是「先改了再說,壞了再修」。但在 NixOS 上,你有更優雅的選擇:
sudo nixos-rebuild build-vm
這個指令會根據你目前的 configuration.nix,建構一個完整的 NixOS 系統,然後包裝成一個 QEMU 虛擬機映像。你可以直接啟動這台 VM,在裡面驗證你的配置是否正確——完全不影響你的主機。
第一次使用 build-vm
讓我們來實際操作一次。假設你想在系統裡加入 htop 這個套件,並且啟用 Nginx:
首先,編輯你的 configuration.nix:
# /etc/nixos/configuration.nix
{ config, pkgs, ... }:
{
# ... 既有的設定 ...
# 加入 htop
environment.systemPackages = with pkgs; [
vim
git
htop # 新增這行
];
# 啟用 Nginx
services.nginx.enable = true;
services.nginx.virtualHosts."localhost" = {
root = "/var/www";
};
}
接著,不要急著 switch,先用 build-vm 試試看:
sudo nixos-rebuild build-vm
建構完成後,你會看到類似這樣的輸出:
building the system configuration...
Done. The virtual machine can be started by running
/nix/store/xxxx-nixos-vm/bin/run-nixos-vm
啟動虛擬機
直接執行輸出的路徑就能啟動 VM:
sudo /nix/store/xxxx-nixos-vm/bin/run-nixos-vm -nographic
QEMU 會開啟一個視窗,裡面跑著一台完整的 NixOS 系統。你可以在裡面登入、檢查套件是否安裝、service 是否正常啟動。
💡 小提醒:
build-vm預設會使用你的configuration.nix,但 VM 的 root 密碼預設是空的(直接按 Enter 就能登入)。
如果需要指定密碼,可以在配置中加入:
users.users.root.initialPassword = "test";
如果你覺得懶得每次測試都要打密碼,可以在 confiuration.nix 中新增以下設定
services.getty.autologinuser="root";
如果需要退出 vm 可以執行以下指令
poweroff # 或者 shutdown -h now
在 VM 中測試配置變更
啟動 VM 之後,你可以進行以下驗證:
確認套件已安裝
# 在 VM 裡面執行
which htop
htop --version
確認 service 狀態
# 在 VM 裡面執行
systemctl status nginx
curl http://localhost
確認系統設定
# 在 VM 裡面執行
nixos-version
cat /etc/os-release
如果一切符合預期,你就可以放心地回到主機上執行:
sudo nixos-rebuild switch
如果發現問題,只要關掉 VM 視窗,修改 configuration.nix,再重新 build-vm 即可。完全零風險。
build-vm 的限制
雖然 build-vm 非常實用,但有幾點要留意:
- 硬體相關的設定(如 GPU driver、Wi-Fi 驅動程式)在 VM 裡可能無法正確測試
- 磁碟掛載 (mount)、分割區 (partition) 等設定在 VM 裡不會反映真實硬體
- VM 的效能會比實機慢,尤其是圖形介面
- 需要安裝 QEMU(NixOS 預設通常已包含)
Rollback 回滾實作
情境:switch 之後發現問題
假設你已經執行了 nixos-rebuild switch,結果新的配置有問題——也許某個 service 啟動失敗,或是桌面環境壞掉了。別緊張,NixOS 的 rollback 機制讓你可以秒速還原。
方法一:從 bootloader 回滾
最簡單的方式:重新開機,在 GRUB 選單中選擇上一個 generation。
GRUB 選單會列出所有可用的 generation,格式大概像這樣:
NixOS - Configuration 3 (current)
NixOS - Configuration 2
NixOS - Configuration 1
選擇上一個可正常運作的 generation,就能立刻回到那個狀態。
方法二:使用指令回滾
如果系統還能正常進入,可以直接用指令切換:
# 切換到上一個 generation
sudo nixos-rebuild switch --rollback
這個指令會立刻將系統切換到前一個 generation,不需要重新開機。
方法三:切換到指定的 generation
如果你想跳回更早的某個 generation,可以這樣操作:
# 先列出所有 generation
sudo nix-env --list-generations --profile /nix/var/nix/profiles/system
# 切換到指定的 generation(例如第 2 個)
sudo /nix/var/nix/profiles/system-2-link/bin/switch-to-configuration switch
切換完成後,系統就會回到 generation 2 的狀態,包含當時的所有套件版本、service 設定、系統參數等等。
完整的安全工作流程
綜合今天學到的內容,推薦的配置變更流程如下:
修改 configuration.nix
│
▼
nixos-rebuild build-vm
│
▼
啟動 VM 測試
┌────┴────┐
│ │
通過 ✅ 失敗 ❌
│ │
▼ ▼
switch 修改配置
再測試
用一行指令表示就是:
# 1. 先測試
sudo nixos-rebuild build-vm
# 2. 確認沒問題再正式套用
sudo nixos-rebuild switch
# 3. 如果出問題,立即回滾
sudo nixos-rebuild switch --rollback
補充:rebuild 的幾種模式
在往下走之前,讓我們整理一下 nixos-rebuild 常用的幾個子指令:
| 指令 | 說明 |
|---|---|
nixos-rebuild switch | 建構新 generation 並立即切換,同時更新 bootloader |
nixos-rebuild boot | 建構新 generation 並更新 bootloader,但不立即切換(下次開機才生效) |
nixos-rebuild test | 建構新 generation 並立即切換,但不更新 bootloader(重開機會回到舊版) |
nixos-rebuild build | 只建構,不切換也不更新 bootloader(純粹確認能不能成功 build) |
nixos-rebuild build-vm | 建構一個可在 QEMU 中執行的 VM,用來測試配置 |
每個指令適用於不同的情境:
- 日常更新:
switch - 想先確認能 build:
build - 想在 VM 裡測試:
build-vm - 想下次開機才套用:
boot - 想暫時測試但重開機能還原:
test
小結
今天我們學到了 NixOS 版本管理的三大支柱:
- Generation 機制:每次 rebuild 都會產生一個不可變的系統快照,讓你永遠有回頭路。
- build-vm 測試:在正式套用之前,用虛擬機驗證你的配置,零風險實驗。
- Rollback 回滾:就算 switch 之後出了問題,一個指令就能回到上一個正常的狀態。
這三個機制加在一起,構成了 NixOS 最讓人安心的「安全網」。你再也不用害怕「改壞系統」這件事了——因為每一個改動都是可追蹤、可測試、可還原的。
明日預告
Day 5:Nix Language 快速入門(上)
到目前為止,我們一直在修改 configuration.nix,但對裡面那些 { config, pkgs, ... }: 之類的語法還是似懂非懂。明天我們就來正式認識 Nix language——NixOS 背後的函數式語言。我們會從基本型別、變數綁定 (let-in) 開始,帶你讀懂那些看起來有點陌生的語法。
📌 今日指令速查表
# 列出所有 generation sudo nix-env --list-generations --profile /nix/var/nix/profiles/system # 比較 generation 差異 nix profile diff-closures --profile /nix/var/nix/profiles/system # 在 VM 中測試配置 sudo nixos-rebuild build-vm # 正式套用配置 sudo nixos-rebuild switch # 回滾到上一個 generation sudo nixos-rebuild switch --rollback # 清理舊的 generation sudo nix-collect-garbage --delete-older-than 30d