目的
- 避免新安装的麻烦:在新机器上只要有efi/esp分区和一个大分区,只需要复制一个efi文件和一个虚拟磁盘文件即可完成安装。甚至在github上可以直接下载本文的构建结果。
- 可以物理机和虚拟机启动同一个系统:在win/macos下可以用虚拟机启动nixos,也可以物理机直接启动。
- 比ventoy更可靠,完全使用nix和grub的机制
同一个 nixos.raw 有三种用法:
- 用
dd写到 U 盘或硬盘,开机从它启动; - 放在任意 NTFS 分区的
OS-img/nixos.raw,由另外一个定制的 EFI 文件找到并完成后续启动国产; - 直接作为虚拟磁盘,交给 VMware、VirtualBox、Hyper-V 或 QEMU。
三种方式启动后是同一个系统,没有任何区别。
两个配置的分工
仓库里和 Raw OS 相关的配置有两类,职责不同。
raw_os_core_img_creat(machine/raw_os/raw-os-core-img-creat.nix)只负责生成基础镜像(体积为10G 压缩后后1.6G左右)。系统里只有常用的命令行工具、NetworkManager、fish 和 nixos 用户(用户名密码都是 nixos,root用户的密码也是nixos,),足够第一次启动后做后续配置。为了首次使用方便,基础镜像默认自动登录。
raw_os_work_amd 和 raw_os_work_intel是运行在镜像里的日常环境。区别是 amd 或 intel tag 决定 IOMMU、KVM、VFIO 的配置以及部分驱动等,同时加载桌面、开发、输入法等模块。
也就是 raw_os_core_img_creat 用来创建 nixos.raw这个文件,方便部署。然后启动系统在这个基础系统内 运行sudo nixos-rebulid switch --flake .#raw_os_work_amd/intel 来完成日用环境
镜像的结构
镜像内部分区
- 分区 1
esp:vfat,固定 1 GiB,分区标签nixos-esp。 - 分区 2
root:btrfs,固定 10 GiB,分区标签nixos-root,挂载参数带compress=zstd。
ESP 里放四个文件:
| |
构建镜像
| |
命令输出一个 /nix/store/...-nixos-disk-image 目录,里面的 nixos.raw 就是最终镜像。把它复制到目标位置,例如 NTFS 分区:
| |
写盘启动也是同一个文件:
| |
外部 EFI
想在不改分区表的前提下启动这个镜像,需要第二个 EFI 文件 rawOsExternalBootEfi。它同样是 standalone GRUB,但内嵌的配置不一样:它会在 NTFS 分区上搜索 /OS-img/nixos.raw,用 loopback 把镜像映射成一块磁盘,然后加载 (raw,gpt1)/EFI/NixOS/raw-os/grub.cfg。同时会搜索是否有Windows的引导,然后构建一个启动菜单里面包括:nixos.raw内的多个generation 以及自动搜索启动Windows
构建在各个efi的 方法是执行脚本:(具体参考仓库:https://github.com/joyanhui/myos)
| |
外部 EFI 只需要部署一次,它不保存 nixos-current.efi、grub.cfg 和 nixos-generation-*.efi,这些文件都在镜像内部。只有改了外部 EFI 的搜索路径、启动逻辑、内嵌模块,或者更新 nixpkgs 后需要重新验证、文件损坏时,才需要重新构建并手动替换。
从 NTFS 启动时根分区从哪里来
外部 EFI 把镜像当作一块磁盘来读,此时 NixOS 内核已经启动,但镜像里的分区还没有出现在 /dev 下,因为镜像内的 GPT 分区要先由 loop 设备导出。这部分由 initrd 完成。
raw-os-runtime.nix 在 initrd 里注册了 nixos-img-loop-for-root.service,Type=oneshot,wantedBy = [ "initrd.target" ],并且排在 initrd-root-device.target 和 sysroot.mount 之前。脚本的逻辑是:
- 等 10 秒,直到出现
/dev/vd*、/dev/sd*、/dev/nvme*n*p*这类块设备; - 检查是否有分区的标签已经是
nixos-root,有就直接退出——这是写盘启动和虚拟机启动的情况; - 否则逐个尝试用 ntfs3 挂载宿主的分区,找到
OS-img/nixos.raw的所在的分区; - 对镜像执行
losetup -fP,再udevadm trigger和udevadm settle,等 loop 分区出现; - 按标签给每个 loop 分区建立
/dev/disk/by-partlabel/<label>链接。
在运行中的系统里更新
在已经启动的 Raw OS 内,进入仓库目录执行:
| |
Intel 机器把目标换成 raw_os_work_intel。
raw-os-runtime.nix 通过 boot.loader.external.installHook 挂上了 installRawBoot 脚本,nixos-rebuild switch 在最后阶段执行它。它做的事情是:
- 读取
$toplevel/boot.json,取出 kernel、initrd、init、initrdSecrets 和 kernelParams; - 把 initrd 复制到临时目录,如果配置了 initrd secrets 就追加进去,避免直接修改 store 里的文件;
- 调用
ukify build,用linux<arch>.efi.stub作 stub,cmdline 拼成init=<init> <kernelParams>,生成 UKI; - 扫描
/nix/var/nix/profiles/system-*-link,按 generation 编号倒序排列,并用readlink -f的结果匹配当前 toplevel,得到当前 generation 的编号; - 选出当前 generation 加上至多 8 个 generation,为每个生成
nixos-generation-<n>.efi; - 生成新的
grub.cfg:第一项是nixos-current.efi,之后是各 generation 的倒序菜单,最后是 Windows Boot Manager; - 先删除不在列表里的旧 generation 文件,然后依次安装 current、各 generation、
grub.cfg,再清理一次,最后安装EFI/BOOT/BOOTX64.EFI; - 所有文件都是先写临时文件再
mv -f替换,中途出错不会留下半个 UKI。
nixos-current.efi 单独保留,不占 generation 的数量限制。boot.loader.efi.canTouchEfiVariables = false,整个过程不会修改 UEFI 的启动变量。
ESP 只有 1 GiB,所以脚本选择先清理旧 generation 再安装新文件,这样不需要额外空间来暂存新的 UKI。代价是如果安装过程失败,被删掉的旧 generation 已经找不回来,重启后菜单里能回滚的项会变少。
在虚拟机里启动
nixos.raw 是标准格式的虚拟磁盘,虚拟机软件基本都能直接用。
- VMware Workstation 和 ESXi 可以直接把 raw 文件当作虚拟磁盘,也可以创建一个 VMDK 映射它,客户机固件设置为 UEFI。VMware 的虚拟显卡性能在这几个方案里最好,关闭动态壁纸和毛玻璃,装好客户机工具之后,niri 的体验接近物理机。
- VirtualBox 原生支持 raw 磁盘,只需要把系统设置成 UEFI 启动。
- Hyper-V 要先用 Arsenal-Image-Mounter 之类的工具把镜像挂载成物理磁盘,在磁盘管理里把它设为脱机,然后在第二代虚拟机的磁盘设置里选择这块物理硬盘。仓库里还有一个
additional-foot-to-raw-and-renameVHD.sh,会给 raw 文件加上 VHD 尾部并改名为.vhd,第一代虚拟机可以直接使用。 - QEMU 直接用
-drive format=raw,详见下面的测试命令。
在 Windows 下用 QEMU 启动的完整命令:
| |
io.systemd.stub.kernel-cmdline-extra 是给 UKI 追加内核参数,这里加了 nomodeset,避免在虚拟显卡上因为没有对应驱动而黑屏。如果启动后停在 EFI Shell,输入 FS0:\EFI\BOOT\BOOTX64.EFI 手动启动。OVMF 固件可以从 QEMU 安装目录获取,仓库的 kvm-config/ 下也有副本,用法见 KVM 虚拟机使用 OVMF 模式 UEFI 启动。
扩容
根分区固定 10 GiB,装上日常环境后空间会比较紧张。仓库里有两个等价的实现:镜像自带的 resize-raw-image(和本仓库的 docs_script/resize-raw-image.sh,已经加入系统包),以及 Windows 下的 resize-raw-image.ps1。
相关文章
- NixOS 复现安装(flake 模式) — 传统安装流程,理解
nixos-install之后再来看构建镜像会更清楚 - 启用 flakes — Raw OS 的所有构建命令都依赖 flake
- NixOS 下使用 KVM 和显卡直通 — 运行在 Raw OS 里的工作环境会用到这些配置
- 把 Arch Linux 安装到 vhd/raw 文件并从物理机启动 — 同一类做法,用 Ventoy 和 vtoyboot 引导磁盘文件
- 基于 Ventoy 启动的 Linux 虚拟磁盘扩容 — 磁盘文件扩容的另一种实现,可以对照本文的扩容脚本
- KVM 虚拟机使用 OVMF 模式 UEFI 启动 — 测试 Raw OS 时使用的固件和相关参数