本文是 KVM 虚拟化专题的第 6 篇子文档,聚焦 NixOS 作为 KVM 宿主机的声明式配置与排障。通用概念(QEMU/KVM 原理、libvirt 架构、virt-manager 操作)请先阅读 通用安装指南。
1. NixOS KVM 配置全景#
1.1 为什么 NixOS 特别适合跑 KVM
NixOS 的声明式配置模型与 KVM 虚拟化的契合度极高,主要体现在四个方面:
| 优势 | 说明 |
|---|---|
| 单文件全量配置 | 内核模块、libvirtd、QEMU、virt-manager、网络、防火墙全部聚合在一个 configuration.nix 或 flake 中,不再分散于 /etc/libvirt/、/etc/modprobe.d/、systemd unit 手动编写等多处 |
| 可复现性 | 换机、重装、多机部署时,应固定 nixpkgs 与模块输入并准备宿主硬件/网络差异;VM 磁盘、libvirt 运行态、NVRAM 和 TPM 另行恢复 |
| 声明式 VM 定义 | 搭配社区项目 NixVirt,甚至可以用 Nix 语法声明式定义虚拟机(domain XML 的 nix 表达),做到 "VM as Code" |
| 原子升级与回滚 | 升级内核 / QEMU / libvirt 后如果 VM 出问题,直接从 bootloader 选择上一代配置回滚;不能回滚 VM 磁盘、NVRAM 和 TPM 中的外部状态 |
1.2 两步启用
在 NixOS 上获得一个可用的 KVM 环境,最少只需要两行:
virtualisation.libvirtd.enable = true;
programs.virt-manager.enable = true;加上把用户加入 libvirtd 组:
users.users.user.extraGroups = [ "libvirtd" ];先按项目规则评估与构建;activation/reboot 由管理员单独决定。应用配置后重新登录使组成员身份生效,再检查 libvirt URI、KVM 和网络;本文没有 rebuild 或运行 VM。
libvirtd.enable = true会自动拉入 QEMU/KVM 用户态组件,无需单独声明pkgs.qemu_kvm。但如果需要特定版本的 QEMU 或额外的 QEMU 功能(如 virtiofsd),则需要通过qemu.package和qemu.vhostUserPackages显式指定。
2. 声明式配置全解#
以下是一份面向 NixOS 配置模型,并以本次核对的 nixos-unstable 模块为接口快照(实际项目应以 flake.lock 固定的 nixpkgs 为准) 的完整 KVM 宿主机配置,按功能分组,每组附解释。
2.1 内核模块与启动参数
{ config, pkgs, lib, ... }:
{
# ===== 内核模块 =====
# AMD CPU: "kvm-amd";Intel CPU: "kvm-intel"
# 按宿主 CPU 选择模块,下面只演示 AMD 分支
boot.kernelModules = [ "kvm-amd" ];
# ===== IOMMU 开启(PCI 直通前提) =====
# AMD IOMMU 通常由固件开启后自动初始化;不要套用不存在的 amd_iommu=on 值
# Intel: "intel_iommu=on"
# iommu=pt 可让宿主 DMA 使用 passthrough 默认域;需评估隔离与性能,非通用默认
boot.kernelParams = [
"iommu.strict=1"
# "iommu=pt" # 仅在明确评估宿主 DMA 隔离后启用
];
# ===== 嵌套虚拟化(可选) =====
# 允许在 VM 内部再运行 KVM(比如在 VM 里跑 minikube 或测试 nested KVM)
boot.extraModprobeConfig = ''
options kvm_amd nested=1
'';
}各参数说明:
kvm-amd:AMD-V 硬件虚拟化内核模块。加载后可检查:lsmod | grep kvm,应看到kvm_amd和kvm。iommu.strict=1:控制 DMA unmap 的同步失效,不是启用 AMD IOMMU 的开关。AMD 通常由固件和内核自动初始化;验证 IOMMU group、interrupt remapping 与 VFIO。SVM 是 CPU 虚拟化开关,AMD-Vi 是 IOMMU 功能。iommu=pt:可改变宿主设备的默认 DMA 域,不能宣称总有性能收益;它也改变隔离边界,应按威胁模型选择。nested=1:允许 VM 内部使用svm指令,即嵌套虚拟化。
验证 IOMMU 是否生效:
dmesg | grep -i iommu,应看到AMD-Vi: Interrupt remapping enabled或类似输出。验证 IOMMU 分组:find /sys/kernel/iommu_groups/ -type l | sort -V。
2.2 libvirtd 主配置
virtualisation.libvirtd = {
enable = true;
# ===== QEMU 子配置 =====
qemu = {
# QEMU 包选择:pkgs.qemu_kvm 内置 KVM 加速支持
package = pkgs.qemu_kvm;
# system libvirt 与 QEMU 运行身份分开。此接口快照默认 true。
# true: QEMU 以 root 运行;false: 使用 qemu-libvirtd 用户/组。
# 迁移现有镜像时检查读写、目录遍历与安全标签;这并非整个 daemon rootless。
runAsRoot = false;
# Software TPM 2.0 模拟(满足 Windows 11 TPM 条件的一种后端)
swtpm.enable = true;
# vhost-user 后端包:virtiofsd 用于宿主机-虚拟机共享目录
vhostUserPackages = with pkgs; [ virtiofsd ];
};
# ===== 宿主关机时 libvirt-guests 的 VM 处理 =====
# "shutdown":发送 ACPI 关机信号
# "suspend":尝试保存 VM 状态到磁盘;直通等域可能不支持,必须验证
onShutdown = "shutdown";
};
# ===== SPICE USB 重定向 =====
# 允许将宿主机 USB 设备热插拔到 VM 中
virtualisation.spiceUSBRedirection.enable = true;重要变更:OVMF 配置 本次核对的 nixos-unstable 模块已移除旧
qemu.ovmf.enable/packages配置接口,固件元数据来自选择的 QEMU 包;实际迁移以项目固定 revision 的 option 定义为准。旧配置在此接口中会触发 assertion。固件路径也发生了变化:
旧路径 新路径 /run/libvirt/nix-ovmf/OVMF_CODE.fd/run/libvirt/nix-ovmf/edk2-x86_64-code.fd/run/libvirt/nix-ovmf/OVMF_VARS.fd/run/libvirt/nix-ovmf/edk2-i386-vars.fd/run/libvirt/nix-ovmf/OVMF_CODE.ms.fd/run/libvirt/nix-ovmf/edk2-x86_64-secure-code.fd新建域可使用
<os firmware="efi">自动选择固件。已有 VM 必须先备份 NVRAM/TPM 与加密恢复资料,再核对 CODE/VARS 兼容性;不要直接删除<nvram>重置已有状态。表中路径是历史示例,实际从当前固件 metadata 与domcapabilities确认。
2.3 GUI 与客户端工具
# ===== virt-manager GUI =====
programs.virt-manager.enable = true;
# ===== 宿主机 QEMU Guest Agent 与 SPICE Agent =====
# qemuGuest: 宿主机作为 VM 时可被 hypervisor 控制
# spice-vdagentd: 宿主机上的 SPICE agent daemon(剪贴板共享等)
# 仅当这个 NixOS 系统本身是需要此集成的 guest 时启用:
# services.qemuGuest.enable = true;
# services.spice-vdagentd.enable = true;
# ===== 额外系统包 =====
environment.systemPackages = with pkgs; [
virt-viewer # SPICE/VNC 独立控制台
spice-gtk # SPICE GTK 客户端库
win-virtio # Windows 客户机 virtio 驱动 ISO
win-spice # Windows 客户机 SPICE guest tools
];
# ===== 用户组 =====
users.users.user.extraGroups = [ "libvirtd" ];
win-virtio包提供virtio-win.iso,实际路径由安装 profile 与包布局决定;systemPackages 应从/run/current-system/sw/share/或当前 store 包中定位,不能假定在用户 profile 中。创建 Windows VM 时将此 ISO 作为第二个 CDROM 挂载,即可在安装过程中加载 virtio 存储和网络驱动。
2.4 防火墙配置
不要把 trustedInterfaces = [ "virbr0" ] 当默认必要条件;它会放宽客体对宿主服务的入站访问。先检查 libvirt 网络的 nftables/iptables 规则及 NixOS 防火墙,按实际需求只允许 DHCP/DNS 等服务。例如确有需要时:
networking.firewall.interfaces.virbr0 = {
allowedUDPPorts = [ 53 67 ];
allowedTCPPorts = [ 53 ];
};
}这是 DNS/DHCP 入站示例,不是所有转发/ NAT 问题的统一修复;以实际后端和规则验证流量。
3. 网络配置#
3.1 默认 NAT 网络自动激活
默认网络的存在、active 与 autostart 状态是 libvirt 运行态。此接口快照会在首次配置时复制包内默认网络定义而保留已有文件;仍需显式查看 net-list --all、net-info default 与网段冲突。对于已有持久网络,可以在授权管理操作中设置 net-autostart default;这不是 Nix 对全部 domain/network 状态的声明式所有权。
若确实需要服务来激活已存在的网络,下面示例先检测 active 状态,避免重复 net-start;不在服务停止时 net-destroy,以免 rebuild/restart 中断客体网络。
systemd.services.libvirt-default-network = {
description = "Activate existing libvirt default NAT network";
after = [ "libvirtd.service" ];
requires = [ "libvirtd.service" ];
wantedBy = [ "multi-user.target" ];
path = [ pkgs.libvirt pkgs.gawk ];
environment.LC_ALL = "C";
script = ''
active=$(virsh -c qemu:///system net-info default | awk '/^Active:/ {print $2}')
if [ "$active" != "yes" ]; then
virsh -c qemu:///system net-start default
fi
'';
serviceConfig = { Type = "oneshot"; RemainAfterExit = true; };
};若项目改用模块化 daemon,依赖需对应实际服务;不要同时宣称单体和模块化都默认启用。网络 XML 若要求完全声明式,可评估 NixVirt,并明确它对已有资源的接管范围。
3.2 桥接网络(Bridged Mode)
如果希望 VM 直接接入宿主机所在的物理网络(与宿主机同级 IP),使用桥接模式替代默认 NAT:
networking.bridges.br0 = {
interfaces = [ "enp3s0" ]; # 替换为你的物理网卡名
};
networking.interfaces.br0.useDHCP = true;
networking.interfaces.enp3s0.useDHCP = false; # 物理网卡不再独立获取 IP然后用 virsh net-define 创建一个指向 br0 的 libvirt 网络,或在 virt-manager 中为 VM 网卡选择 "Bridge device" 并填入 br0。
桥接模式下宿主机将失去对物理网卡的直接控制(IP 移到 br0 上)。不要在远程 SSH 连接的机器上直接操作,配置错误会导致断连。先在本地验证。
3.3 网络拓扑总览
flowchart LR
VM_A[VM A / 192.0.2.x] --> virbr0[virbr0 NAT]
VM_B[VM B / 192.0.2.x] --> virbr0
virbr0 --> Host[宿主机网络栈]
Host --> enp3s0[物理网卡 enp3s0]
VM_C[VM C / 198.51.100.x] --> br0[br0 Bridge]
Host --> br0
br0 --> enp3s04. dconf 配置:消除首次连接延迟#
预设 URI/autoconnect 可以减少手动连接步骤,但不能保证消除所有启动延迟。NixOS dconf profile、Home Manager dconf.settings 和 GUI 用户状态的优先级不同;只设默认值时不要意外锁定。下面 lockAll 若启用会限制用户改写,应按需要选择。
部分用户在首次启动 virt-manager 时会遇到界面卡在 "Connecting..." 转圈的情况。原因是 virt-manager 没有预设的 libvirt 连接 URI,需要手动输入 qemu:///system 才能连接。
通过 dconf 预先写入 autoconnect 配置可解决此问题:
programs.dconf = {
enable = true;
profiles.user.databases = [
{
lockAll = false; # 这里只提供默认值;需要管理锁定时另行决定
settings = with lib.gvariant; {
"org/virt-manager/virt-manager/connections" = {
autoconnect = [ "qemu:///system" ];
uris = [ "qemu:///system" ];
};
};
}
];
};
programs.dconf.profiles是 NixOS 系统模块接口;Home Manager 使用dconf.settings写入用户配置。两者作用域和优先级不同,不应将系统 profile 称为 Home Manager 风格。只有实际遇到连接配置问题才预设相关项,不能仅凭 Wayland 或打包方式推定故障。Home Manager 示例:# Home Manager 版 dconf.settings = { "org/virt-manager/virt-manager/connections" = { autoconnect = [ "qemu:///system" ]; uris = [ "qemu:///system" ]; }; };
5. AMD GPU 直通与显示方案#
5.1 宿主能力清单
配置依赖设备能力而非某台机器的型号。先记录 CPU 虚拟化、固件 AMD-Vi/IOMMU、内核 driver、IOMMU group、可独立复位的 GPU/音频功能,以及替代显示或带外管理路径。
| 能力 | 检查方向 |
|---|---|
| CPU | AMD-V/SVM;嵌套环境还需宿主开放 |
| GPU | 目标设备功能与 driver、ROM、reset 支持 |
| 内核驱动 | 常见 AMD 显示驱动为 amdgpu |
| IOMMU | AMD-Vi、interrupt remapping、实际 group 隔离 |
| 恢复 | 独立显示/控制路径及正常启动 generation |
5.2 IOMMU 与 VFIO 基础配置
boot.kernelParams = [
"iommu.strict=1"
"iommu=pt"
# vfio-pci.ids 按需添加,格式:vendor:device
# 示例占位符:VGA 与相关音频功能须从实际设备清单确认:
# "vfio-pci.ids=<VENDOR_DEVICE_ID>,<VENDOR_DEVICE_ID>"
];
# 提前加载 VFIO 模块(在显卡驱动之前抢占设备)
boot.initrd.kernelModules = [
"vfio_pci"
"vfio"
"vfio_iommu_type1"
];获取 PCI 设备的 vendor:device ID:
lspci -nn | grep -i vga # 显卡 lspci -nn | grep -i audio # 声卡(通常需要一起直通)使用
<GPU_VENDOR_DEVICE_ID>、<GPU_AUDIO_VENDOR_DEVICE_ID>占位符;不能复制别人的 ID。ID 匹配可能包含多块同型号设备,必要时按 BDF 与 driver_override 设计绑定。
5.3 AMD GPU Reset Bug 与 vendor-reset
部分 AMD GPU(尤其是 dGPU,iGPU 较少遇到)存在 "reset bug":VM 关闭后 GPU 无法被干净重置,导致第二次启动 VM 时报错或死机。
仅对受支持设备考虑的方案:先核对 vendor-reset 的支持列表与内核兼容,再决定加载:
boot.extraModulePackages = with config.boot.kernelPackages; [ vendor-reset ];
boot.kernelModules = [ "kvm-amd" "vendor-reset" ];vendor-reset 只支持特定设备及内核组合。出现 reset failure 时先核对项目支持列表,不应将“较新 GPU”视为自动支持,也不能保证模块解决 reset。
5.4 单 GPU 直通(Specialisation 方案)
本节是需要单独验收的引导配置示意。绑定唯一 GPU 或黑名单 amdgpu 可能让宿主失去全部本地显示;提前准备已验证的恢复 generation 与独立管理路径。video=efifb:off 不保证覆盖现代 simpledrm/固件 framebuffer;设备 reset 与宿主重新绑定也必须测试。specialisation 合并语义与实际 kernelParams 要在构建前检查。
如果你希望将 iGPU 直通给 Windows VM 玩游戏,而宿主机 NixOS 在直通期间失去显示,可以使用 NixOS 的 specialisation 功能创建一个"VFIO 模式"引导项:
specialisation.VFIO.configuration = {
system.nixos.tags = [ "VFIO" ];
boot.kernelParams = [
"iommu.strict=1"
# "iommu=pt" # 仅在明确评估宿主 DMA 隔离后启用
"vfio-pci.ids=<VENDOR_DEVICE_ID>,<VENDOR_DEVICE_ID>" # 替换为实际 ID
"video=efifb:off" # 禁用 EFI 帧缓冲
];
boot.initrd.kernelModules = [ "vfio_pci" "vfio" "vfio_iommu_type1" ];
boot.blacklistedKernelModules = [ "amdgpu" ];
};重启后在 systemd-boot 菜单中会多出一个 "VFIO" 选项,选择它即可进入 VFIO 直通模式。常规 generation 可作为恢复选择,但磁盘/固件外部状态不会随 generation 自动回滚。
5.5 virtio-gpu + virgl(3D 加速替代方案)
如果不做完整 GPU 直通,但仍需要 Linux 客户机的 3D 加速,可以使用 virtio-gpu 配合 virgl(OpenGL);Vulkan/Venus 是需另外验证的版本与配置路径:
在 virt-manager 中为 VM 的 Video 设备选择:
- Model:
Virtio - 勾选
3D acceleration
或者直接在域 XML 中:
<video>
<model type="virtio" heads="1" primary="yes">
<acceleration accel3d="yes"/>
</model>
</video>virtio-gpu/virgl 与 QXL/SPICE 对应不同显示路径;适用性和性能按客体桌面、驱动、GL支持与实际负载验证。
6. NixOS 常见问题与排障#
6.1 问题速查表
| 问题 | 症状 | 解决方案 |
|---|---|---|
| 用户组未生效 | "polkit agent unavailable" / "permission denied connecting to libvirtd" | 确认 extraGroups = ["libvirtd"],然后注销重新登录(不是 newgrp) |
| default 网络未激活 | virsh net-list 输出为空 / "network 'default' is not active" |
添加 3.1 节 的 systemd oneshot 服务 |
| virt-manager 卡在连接 | 界面持续显示 "Connecting..." 转圈 | 添加 第 4 节 的 dconf autoconnect 配置 |
| OVMF 固件找不到 | "Unable to complete install: 'Cannot access firmware'" | 确保 libvirtd.enable = true(OVMF 会自动包含);旧 VM 需删除 <loader> 和 <nvram> 标签 |
| QEMU 图形性能差 | VM 界面卡顿、鼠标漂移 | lsmod | grep kvm 确认 kvm_amd 已加载;VM 内使用 virtio 驱动;Wayland 上尝试 GDK_BACKEND=x11 virt-manager |
| 嵌套虚拟化不可用 | VM 内 lsmod | grep kvm 无输出 |
添加 boot.extraModprobeConfig = "options kvm_amd nested=1" 并重启 |
| KVM 模块未加载 | modprobe: ERROR: could not insert 'kvm_amd' |
确认 BIOS/UEFI 中 SVM Mode 已启用(AMD)/ VT-x 已启用(Intel) |
| SPICE 在 Wayland 下渲染异常 | 窗口闪烁、输入延迟 | 尝试 GDK_BACKEND=x11 virt-manager;或在 virt-manager 中为 VM 切换为 QXL Video + X11 客户机 |
| USB 重定向不工作 | SPICE 菜单中无 USB 设备 | 确认 virtualisation.spiceUSBRedirection.enable = true;确认用户已安装 spice-gtk |
| VM 关机后 GPU 无法释放 | 第二次启动 GPU 直通 VM 报错 | 核对 reset 日志、设备支持和 vendor-reset 适用性(5.3 节) |
6.2 诊断命令速查
# KVM 模块状态
lsmod | grep kvm
# IOMMU 是否启用
dmesg | grep -i "iommu\|amd-vi"
# IOMMU 分组(验证设备隔离)
find /sys/kernel/iommu_groups/ -type l | sort -V
# libvirtd 状态
systemctl status libvirtd
# 网络状态
virsh net-list --all
# VFIO 设备是否被正确绑定
dmesg | grep vfio
# AMD-V / SVM 是否可用
grep -o 'svm' /proc/cpuinfo | head -16.3 Wayland 宿主机的特殊注意事项
Wayland 宿主上运行 virt-manager 和 SPICE 客户端时,应按 compositor、客户端、guest agent 与客体桌面的版本分别验证:
- virt-manager 启动:如果 GTK 主题缺失导致启动失败,通过 Home Manager 设置指针主题:
home.pointerCursor = { gtk.enable = true; package = pkgs.vanilla-dmz; name = "Vanilla-DMZ"; }; - SPICE 客户端性能:先区分客户端渲染、客体驱动和远程带宽问题;virtio-gpu/virgl 可供本地受支持组合评估,SPICE GL 通常要求本地 Unix socket
- 剪贴板共享:集成取决于客体 agent 与 compositor 的协议支持;分别测试宿主和客体会话,不能将所有 Wayland 组合一概视为不支持,也不能保证切换 X11 就解决全部问题
7. 与 NixOS VM 构建系统的关系#
本节讨论两个互补但不重叠的 NixOS 虚拟化能力:
| 维度 | KVM 宿主机(本文档) | NixOS VM 构建 |
|---|---|---|
| 角色 | 运行通用 VM(Linux/Windows/macOS)的 hypervisor | 使用 Nix 表达式声明式构建 NixOS VM 镜像 |
| 配置方式 | virtualisation.libvirtd.* + virt-manager / virsh |
nixos-build-vm 或 nix-build '<nixpkgs/nixos>' -A vm |
| 典型用途 | 跑 Windows 游戏 / 测试其他发行版 / 多租户隔离 | CI 测试 NixOS 配置、构建云镜像、快速验证 nix 表达式 |
| 产物 | 磁盘镜像 (qcow2/raw) + libvirt domain XML | 可直接启动的 result/bin/run-*-vm 脚本 |
互补关系 你可以在本文档搭建的 KVM 宿主机上运行通过
nixos-build-vm构建的 NixOS VM 镜像。两者使用相同的qemu_kvm后端,只是管理和构建方式不同。详细内容参见 NixOS 虚拟化与部署 MOC。
7.1 快速示例:在 KVM 宿主机上启动 NixOS 构建 VM
# 在任意 NixOS 配置目录下构建 VM
nixos-build-vm
# 构建产物是一个可执行脚本,直接运行
./result/bin/run-nixos-vm此命令会启动一个 QEMU VM,使用你当前的 NixOS 配置,自动分配网络和显示输出。
8. 推荐阅读路径#
KVM 专题内:
- 核心概念与架构 — 理解 KVM/QEMU/libvirt 的分层关系
- 通用安装与初始化 — 跨发行版的 KVM 环境搭建
- 虚拟机创建与管理 — virsh / virt-install 操作
- 图形化管理 — virt-manager 操作
- 存储、网络与 GPU 直通 — 存储池、高级网络、VFIO 直通
跨专题链接:
- NixOS 虚拟化与部署 MOC — NixOS 作为 VM 客户端的构建与部署
参考与延伸
根据技术笔记重新整理,示例使用通用名称。