1 { config, pkgs, lib, ... }:
12 example = "order=scsi0;net0";
14 Default boot device. PVE will try all devices in its default order if this value is empty.
19 default = "virtio-scsi-single";
22 SCSI controller type. Must be one of the supported values given in
23 <https://pve.proxmox.com/wiki/Qemu/KVM_Virtual_Machines>
28 default = "local-lvm:vm-9999-disk-0";
29 example = "ceph:vm-123-disk-0";
31 Configuration for the default virtio disk. It can be used as a cue for PVE to autodetect the target storage.
32 This parameter is required by PVE even if it isn't used.
43 type = types.ints.positive;
50 type = types.ints.positive;
57 type = types.enum [ "seabios" "ovmf" ];
60 Select BIOS implementation (seabios = Legacy BIOS, ovmf = UEFI).
67 default = "nixos-${config.system.nixos.label}";
72 additionalSpace = mkOption {
77 additional disk space to be added to the image if diskSize "auto"
86 Size of the boot partition. Is only used if partitionTableType is
87 either "efi" or "hybrid".
95 The size of the disk, in megabytes.
96 if "auto" size is calculated based on the contents copied to it and
97 additionalSpace is taken into account.
102 default = "virtio=00:00:00:00:00:00,bridge=vmbr0,firewall=1";
104 Configuration for the default interface. When restoring from VMA, check the
105 "unique" box to ensure device mac is randomized.
111 example = "/dev/ttyS0";
113 Create a serial device inside the VM (n is 0 to 3), and pass through a host serial device (i.e. /dev/ttyS0),
114 or create a unix socket on the host side (use qm terminal to open a terminal connection).
119 apply = x: if x then "1" else "0";
122 Expect guest to have qemu agent running
126 qemuExtraConf = mkOption {
127 type = with types; attrsOf (oneOf [ str int ]);
129 example = literalExpression ''
136 Additional options appended to qemu-server.conf
139 partitionTableType = mkOption {
140 type = types.enum [ "efi" "hybrid" "legacy" "legacy+gpt" ];
142 Partition table type to use. See make-disk-image.nix partitionTableType for details.
143 Defaults to 'legacy' for 'proxmox.qemuConf.bios="seabios"' (default), other bios values defaults to 'efi'.
144 Use 'hybrid' to build grub-based hybrid bios+efi images.
146 default = if config.proxmox.qemuConf.bios == "seabios" then "legacy" else "efi";
147 defaultText = lib.literalExpression ''if config.proxmox.qemuConf.bios == "seabios" then "legacy" else "efi"'';
150 filenameSuffix = mkOption {
152 default = config.proxmox.qemuConf.name;
153 example = "999-nixos_template";
155 Filename of the image will be vzdump-qemu-''${filenameSuffix}.vma.zstd.
156 This will also determine the default name of the VM on restoring the VMA.
157 Start this value with a number if you want the VMA to be detected as a backup of
166 Whether the VM should accept cloud init configurations from PVE.
169 defaultStorage = mkOption {
170 default = "local-lvm";
174 Default storage name for cloud init drive.
182 Bus/device to which the cloud init drive is attached.
189 cfg = config.proxmox;
190 cfgLine = name: value: ''
191 ${name}: ${builtins.toString value}
193 virtio0Storage = builtins.head (builtins.split ":" cfg.qemuConf.virtio0);
194 cfgFile = fileName: properties: pkgs.writeTextDir fileName ''
196 ${lib.concatStrings (lib.mapAttrsToList cfgLine properties)}
197 #qmdump#map:virtio0:drive-virtio0:${virtio0Storage}:raw:
199 inherit (cfg) partitionTableType;
200 supportEfi = partitionTableType == "efi" || partitionTableType == "hybrid";
201 supportBios = partitionTableType == "legacy" || partitionTableType == "hybrid" || partitionTableType == "legacy+gpt";
202 hasBootPartition = partitionTableType == "efi" || partitionTableType == "hybrid";
203 hasNoFsPartition = partitionTableType == "hybrid" || partitionTableType == "legacy+gpt";
207 assertion = config.boot.loader.systemd-boot.enable -> config.proxmox.qemuConf.bios == "ovmf";
208 message = "systemd-boot requires 'ovmf' bios";
211 assertion = partitionTableType == "efi" -> config.proxmox.qemuConf.bios == "ovmf";
212 message = "'efi' disk partitioning requires 'ovmf' bios";
215 assertion = partitionTableType == "legacy" -> config.proxmox.qemuConf.bios == "seabios";
216 message = "'legacy' disk partitioning requires 'seabios' bios";
219 assertion = partitionTableType == "legacy+gpt" -> config.proxmox.qemuConf.bios == "seabios";
220 message = "'legacy+gpt' disk partitioning requires 'seabios' bios";
223 system.build.VMA = import ../../lib/make-disk-image.nix {
224 name = "proxmox-${cfg.filenameSuffix}";
225 inherit (cfg) partitionTableType;
227 # Build qemu with PVE's patch that adds support for the VMA format
228 vma = (pkgs.qemu_kvm.override {
230 pulseSupport = false;
235 smartcardSupport = false;
236 spiceSupport = false;
237 ncursesSupport = false;
238 libiscsiSupport = false;
241 seccompSupport = false;
242 guestAgentSupport = false;
243 }).overrideAttrs ( super: rec {
244 # Check https://github.com/proxmox/pve-qemu/tree/master for the version
245 # of qemu and patch to use
247 src = pkgs.fetchurl {
248 url = "https://download.qemu.org/qemu-${version}.tar.xz";
249 hash = "sha256-MnCKxmww2MiSYz6paMdxwcdtWX1w3erSGg0izPOG2mk=";
252 # Proxmox' VMA tool is published as a particular patch upon QEMU
253 "${pkgs.fetchFromGitHub {
256 rev = "14afbdd55f04d250bd679ca1ad55d3f47cd9d4c8";
257 hash = "sha256-lSJQA5SHIHfxJvMLIID2drv2H43crTPMNIlIT37w9Nc=";
258 }}/debian/patches/pve/0027-PVE-Backup-add-vma-backup-format-code.patch"
261 buildInputs = super.buildInputs ++ [ pkgs.libuuid ];
262 nativeBuildInputs = super.nativeBuildInputs ++ [ pkgs.perl ];
267 ${vma}/bin/vma create "vzdump-qemu-${cfg.filenameSuffix}.vma" \
268 -c ${cfgFile "qemu-server.conf" (cfg.qemuConf // cfg.qemuExtraConf)}/qemu-server.conf drive-virtio0=$diskImage
270 ${pkgs.zstd}/bin/zstd "vzdump-qemu-${cfg.filenameSuffix}.vma"
271 mv "vzdump-qemu-${cfg.filenameSuffix}.vma.zst" $out/
273 mkdir -p $out/nix-support
274 echo "file vma $out/vzdump-qemu-${cfg.filenameSuffix}.vma.zst" > $out/nix-support/hydra-build-products
276 inherit (cfg.qemuConf) additionalSpace diskSize bootSize;
278 inherit config lib pkgs;
282 growPartition = true;
283 kernelParams = [ "console=ttyS0" ];
285 device = lib.mkDefault (if (hasNoFsPartition || supportBios) then
286 # Even if there is a separate no-fs partition ("/dev/disk/by-partlabel/no-fs" i.e. "/dev/vda2"),
287 # which will be used the bootloader, do not set it as loader.grub.device.
288 # GRUB installation fails, unless the whole disk is selected.
292 efiSupport = lib.mkDefault supportEfi;
293 efiInstallAsRemovable = lib.mkDefault supportEfi;
297 initrd.availableKernelModules = [ "uas" "virtio_blk" "virtio_pci" ];
301 device = "/dev/disk/by-label/nixos";
305 fileSystems."/boot" = lib.mkIf hasBootPartition {
306 device = "/dev/disk/by-label/ESP";
310 networking = mkIf cfg.cloudInit.enable {
311 hostName = mkForce "";
316 cloud-init = mkIf cfg.cloudInit.enable {
318 network.enable = true;
320 sshd.enable = mkDefault true;
321 qemuGuest.enable = true;
324 proxmox.qemuExtraConf.${cfg.cloudInit.device} = "${cfg.cloudInit.defaultStorage}:vm-9999-cloudinit,media=cdrom";