1 { config, lib, pkgs, ... }:
7 cfg = config.virtualisation.libvirtd;
8 vswitch = config.virtualisation.vswitch;
9 configFile = pkgs.writeText "libvirtd.conf" ''
10 auth_unix_ro = "polkit"
11 auth_unix_rw = "polkit"
14 qemuConfigFile = pkgs.writeText "qemu.conf" ''
15 ${optionalString cfg.qemu.ovmf.enable ''
17 "/run/libvirt/nix-ovmf/AAVMF_CODE.fd:/run/libvirt/nix-ovmf/AAVMF_VARS.fd",
18 "/run/libvirt/nix-ovmf/AAVMF_CODE.ms.fd:/run/libvirt/nix-ovmf/AAVMF_VARS.ms.fd",
19 "/run/libvirt/nix-ovmf/OVMF_CODE.fd:/run/libvirt/nix-ovmf/OVMF_VARS.fd",
20 "/run/libvirt/nix-ovmf/OVMF_CODE.ms.fd:/run/libvirt/nix-ovmf/OVMF_VARS.ms.fd"
23 ${optionalString (!cfg.qemu.runAsRoot) ''
24 user = "qemu-libvirtd"
25 group = "qemu-libvirtd"
27 ${cfg.qemu.verbatimConfig}
30 subDirs = list: [ dirName ] ++ map (e: "${dirName}/${e}") list;
32 ovmfModule = types.submodule {
38 Allows libvirtd to take advantage of OVMF when creating new
39 QEMU VMs with UEFI boot.
43 # mkRemovedOptionModule does not work in submodules, do it manually
45 type = types.nullOr types.package;
51 type = types.listOf types.package;
52 default = [ pkgs.OVMF.fd ];
53 defaultText = literalExpression "[ pkgs.OVMF.fd ]";
54 example = literalExpression "[ pkgs.OVMFFull.fd pkgs.pkgsCross.aarch64-multiplatform.OVMF.fd ]";
56 List of OVMF packages to use. Each listed package must contain files names FV/OVMF_CODE.fd and FV/OVMF_VARS.fd or FV/AAVMF_CODE.fd and FV/AAVMF_VARS.fd
62 swtpmModule = types.submodule {
68 Allows libvirtd to use swtpm to create an emulated TPM.
72 package = mkPackageOption pkgs "swtpm" { };
76 qemuModule = types.submodule {
78 package = mkPackageOption pkgs "qemu" {
80 `pkgs.qemu` can emulate alien architectures (e.g. aarch64 on x86)
81 `pkgs.qemu_kvm` saves disk space allowing to emulate only host architectures.
85 runAsRoot = mkOption {
89 If true, libvirtd runs qemu as root.
90 If false, libvirtd runs qemu as unprivileged user qemu-libvirtd.
91 Changing this option to false may cause file permission issues
92 for existing guests. To fix these, manually change ownership
93 of affected files in /var/lib/libvirt/qemu to qemu-libvirtd.
97 verbatimConfig = mkOption {
103 Contents written to the qemu configuration file, qemu.conf.
104 Make sure to include a proper namespace configuration when
105 supplying custom configuration.
121 QEMU's swtpm options.
125 vhostUserPackages = mkOption {
126 type = types.listOf types.package;
128 example = lib.literalExpression "[ pkgs.virtiofsd ]";
130 Packages containing out-of-tree vhost-user drivers.
136 hooksModule = types.submodule {
139 type = types.attrsOf types.path;
142 Hooks that will be placed under /var/lib/libvirt/hooks/daemon.d/
143 and called for daemon start/shutdown/SIGHUP events.
144 Please see https://libvirt.org/hooks.html for documentation.
149 type = types.attrsOf types.path;
152 Hooks that will be placed under /var/lib/libvirt/hooks/qemu.d/
153 and called for qemu domains begin/end/migrate events.
154 Please see https://libvirt.org/hooks.html for documentation.
159 type = types.attrsOf types.path;
162 Hooks that will be placed under /var/lib/libvirt/hooks/lxc.d/
163 and called for lxc domains begin/end events.
164 Please see https://libvirt.org/hooks.html for documentation.
169 type = types.attrsOf types.path;
172 Hooks that will be placed under /var/lib/libvirt/hooks/libxl.d/
173 and called for libxl-handled xen domains begin/end events.
174 Please see https://libvirt.org/hooks.html for documentation.
179 type = types.attrsOf types.path;
182 Hooks that will be placed under /var/lib/libvirt/hooks/lxc.d/
183 and called for networks begin/end events.
184 Please see https://libvirt.org/hooks.html for documentation.
190 nssModule = types.submodule {
196 This option enables the older libvirt NSS module. This method uses
197 DHCP server records, therefore is dependent on the hostname provided
199 Please see https://libvirt.org/nss.html for more information.
203 enableGuest = mkOption {
207 This option enables the newer libvirt_guest NSS module. This module
208 uses the libvirt guest name instead of the hostname of the guest.
209 Please see https://libvirt.org/nss.html for more information.
218 (mkRemovedOptionModule [ "virtualisation" "libvirtd" "enableKVM" ]
219 "Set the option `virtualisation.libvirtd.qemu.package' instead.")
220 (mkRenamedOptionModule
221 [ "virtualisation" "libvirtd" "qemuPackage" ]
222 [ "virtualisation" "libvirtd" "qemu" "package" ])
223 (mkRenamedOptionModule
224 [ "virtualisation" "libvirtd" "qemuRunAsRoot" ]
225 [ "virtualisation" "libvirtd" "qemu" "runAsRoot" ])
226 (mkRenamedOptionModule
227 [ "virtualisation" "libvirtd" "qemuVerbatimConfig" ]
228 [ "virtualisation" "libvirtd" "qemu" "verbatimConfig" ])
229 (mkRenamedOptionModule
230 [ "virtualisation" "libvirtd" "qemuOvmf" ]
231 [ "virtualisation" "libvirtd" "qemu" "ovmf" "enable" ])
232 (mkRemovedOptionModule
233 [ "virtualisation" "libvirtd" "qemuOvmfPackage" ]
234 "If this option was set to `foo`, set the option `virtualisation.libvirtd.qemu.ovmf.packages' to `[foo.fd]` instead.")
235 (mkRenamedOptionModule
236 [ "virtualisation" "libvirtd" "qemuSwtpm" ]
237 [ "virtualisation" "libvirtd" "qemu" "swtpm" "enable" ])
242 options.virtualisation.libvirtd = {
248 This option enables libvirtd, a daemon that manages
249 virtual machines. Users in the "libvirtd" group can interact with
250 the daemon (e.g. to start or stop VMs) using the
251 {command}`virsh` command line tool, among others.
255 package = mkPackageOption pkgs "libvirt" { };
257 extraConfig = mkOption {
261 Extra contents appended to the libvirtd configuration file,
266 extraOptions = mkOption {
267 type = types.listOf types.str;
269 example = [ "--verbose" ];
271 Extra command line arguments passed to libvirtd on startup.
276 type = types.enum [ "start" "ignore" ];
279 Specifies the action to be done to / on the guests when the host boots.
280 The "start" option starts all guests that were running prior to shutdown
281 regardless of their autostart settings. The "ignore" option will not
282 start the formerly running guest on boot. However, any guest marked as
283 autostart will still be automatically started by libvirtd.
287 onShutdown = mkOption {
288 type = types.enum [ "shutdown" "suspend" ];
291 When shutting down / restarting the host what method should
292 be used to gracefully halt the guests. Setting to "shutdown"
293 will cause an ACPI shutdown of each guest. "suspend" will
294 attempt to save the state of the guests ready to restore on boot.
298 parallelShutdown = mkOption {
299 type = types.ints.unsigned;
302 Number of guests that will be shutdown concurrently, taking effect when onShutdown
303 is set to "shutdown". If set to 0, guests will be shutdown one after another.
304 Number of guests on shutdown at any time will not exceed number set in this
309 shutdownTimeout = mkOption {
310 type = types.ints.unsigned;
313 Number of seconds we're willing to wait for a guest to shut down.
314 If parallel shutdown is enabled, this timeout applies as a timeout
315 for shutting down all guests on a single URI defined in the variable URIS.
316 If this is 0, then there is no time out (use with caution, as guests might not
317 respond to a shutdown request).
321 startDelay = mkOption {
322 type = types.ints.unsigned;
325 Number of seconds to wait between each guest start.
326 If set to 0, all guests will start up in parallel.
330 allowedBridges = mkOption {
331 type = types.listOf types.str;
332 default = [ "virbr0" ];
334 List of bridge devices that can be used by qemu:///session
342 QEMU related options.
350 Hooks related options.
358 libvirt NSS module options.
362 sshProxy = mkOption {
366 Weither to configure OpenSSH to use the [SSH Proxy](https://libvirt.org/ssh-proxy.html).
372 ###### implementation
374 config = mkIf cfg.enable {
378 assertion = config.virtualisation.libvirtd.qemu.ovmf.package == null;
380 The option virtualisation.libvirtd.qemu.ovmf.package is superseded by virtualisation.libvirtd.qemu.ovmf.packages.
381 If this option was set to `foo`, set the option `virtualisation.libvirtd.qemu.ovmf.packages' to `[foo.fd]` instead.
385 assertion = config.security.polkit.enable;
386 message = "The libvirtd module currently requires Polkit to be enabled ('security.polkit.enable = true').";
391 # this file is expected in /etc/qemu and not sysconfdir (/var/lib)
392 etc."qemu/bridge.conf".text = lib.concatMapStringsSep "\n"
396 systemPackages = with pkgs; [ libressl.nc iptables cfg.package cfg.qemu.package ];
397 etc.ethertypes.source = "${pkgs.iptables}/etc/ethertypes";
400 boot.kernelModules = [ "tun" ];
402 users.groups.libvirtd.gid = config.ids.gids.libvirtd;
404 # libvirtd runs qemu as this user and group by default
405 users.extraGroups.qemu-libvirtd.gid = config.ids.gids.qemu-libvirtd;
406 users.extraUsers.qemu-libvirtd = {
407 uid = config.ids.uids.qemu-libvirtd;
408 isNormalUser = false;
409 group = "qemu-libvirtd";
412 security.wrappers.qemu-bridge-helper = {
416 source = "${cfg.qemu.package}/libexec/qemu-bridge-helper";
419 programs.ssh.extraConfig = mkIf cfg.sshProxy ''
420 Include ${cfg.package}/etc/ssh/ssh_config.d/30-libvirt-ssh-proxy.conf
423 systemd.packages = [ cfg.package ];
425 systemd.services.libvirtd-config = {
426 description = "Libvirt Virtual Machine Management Daemon - configuration";
428 # Copy default libvirt network config .xml files to /var/lib
429 # Files modified by the user will not be overwritten
430 for i in $(cd ${cfg.package}/var/lib && echo \
431 libvirt/qemu/networks/*.xml \
432 libvirt/nwfilter/*.xml );
435 # shellcheck disable=SC2174
436 mkdir -p "/var/lib/$(dirname "$i")" -m 755
437 if [ ! -e "/var/lib/$i" ]; then
438 cp -pd "${cfg.package}/var/lib/$i" "/var/lib/$i"
442 # Copy generated qemu config to libvirt directory
443 cp -f ${qemuConfigFile} /var/lib/${dirName}/qemu.conf
445 # stable (not GC'able as in /nix/store) paths for using in <emulator> section of xml configs
446 for emulator in ${cfg.package}/libexec/libvirt_lxc ${cfg.qemu.package}/bin/qemu-kvm ${cfg.qemu.package}/bin/qemu-system-*; do
447 ln -s --force "$emulator" /run/${dirName}/nix-emulators/
450 ln -s --force ${cfg.qemu.package}/bin/qemu-pr-helper /run/${dirName}/nix-helpers/
452 ${optionalString cfg.qemu.ovmf.enable (let
453 ovmfpackage = pkgs.buildEnv {
455 paths = cfg.qemu.ovmf.packages;
459 ln -s --force ${ovmfpackage}/FV/AAVMF_CODE{,.ms}.fd /run/${dirName}/nix-ovmf/
460 ln -s --force ${ovmfpackage}/FV/OVMF_CODE{,.ms}.fd /run/${dirName}/nix-ovmf/
461 ln -s --force ${ovmfpackage}/FV/AAVMF_VARS{,.ms}.fd /run/${dirName}/nix-ovmf/
462 ln -s --force ${ovmfpackage}/FV/OVMF_VARS{,.ms}.fd /run/${dirName}/nix-ovmf/
465 # Symlink hooks to /var/lib/libvirt
466 ${concatStringsSep "\n" (map (driver:
468 mkdir -p /var/lib/${dirName}/hooks/${driver}.d
469 rm -rf /var/lib/${dirName}/hooks/${driver}.d/*
470 ${concatStringsSep "\n" (mapAttrsToList (name: value:
471 "ln -s --force ${value} /var/lib/${dirName}/hooks/${driver}.d/${name}") cfg.hooks.${driver})}
472 '') (attrNames cfg.hooks))}
477 RuntimeDirectoryPreserve = "yes";
478 LogsDirectory = subDirs [ "qemu" ];
479 RuntimeDirectory = subDirs [ "nix-emulators" "nix-helpers" "nix-ovmf" ];
480 StateDirectory = subDirs [ "dnsmasq" ];
484 systemd.services.libvirtd = {
485 wantedBy = [ "multi-user.target" ];
486 requires = [ "libvirtd-config.service" ];
487 after = [ "libvirtd-config.service" ]
488 ++ optional vswitch.enable "ovs-vswitchd.service";
490 environment.LIBVIRTD_ARGS = escapeShellArgs (
495 "120" # from ${libvirt}/var/lib/sysconfig/libvirtd
496 ] ++ cfg.extraOptions
499 path = [ cfg.qemu.package pkgs.netcat ] # libvirtd requires qemu-img to manage disk images
500 ++ optional vswitch.enable vswitch.package
501 ++ optional cfg.qemu.swtpm.enable cfg.qemu.swtpm.package;
505 KillMode = "process"; # when stopping, leave the VMs alone
507 OOMScoreAdjust = "-999";
509 restartIfChanged = false;
512 systemd.services.virtchd = {
513 path = [ pkgs.cloud-hypervisor ];
516 systemd.services.libvirt-guests = {
517 wantedBy = [ "multi-user.target" ];
518 path = with pkgs; [ coreutils gawk cfg.package ];
519 restartIfChanged = false;
521 environment.ON_BOOT = "${cfg.onBoot}";
522 environment.ON_SHUTDOWN = "${cfg.onShutdown}";
523 environment.PARALLEL_SHUTDOWN = "${toString cfg.parallelShutdown}";
524 environment.SHUTDOWN_TIMEOUT = "${toString cfg.shutdownTimeout}";
525 environment.START_DELAY = "${toString cfg.startDelay}";
528 systemd.sockets.virtlogd = {
529 description = "Virtual machine log manager socket";
530 wantedBy = [ "sockets.target" ];
531 listenStreams = [ "/run/${dirName}/virtlogd-sock" ];
534 systemd.services.virtlogd = {
535 description = "Virtual machine log manager";
536 serviceConfig.ExecStart = "@${cfg.package}/sbin/virtlogd virtlogd";
537 restartIfChanged = false;
540 systemd.sockets.virtlockd = {
541 description = "Virtual machine lock manager socket";
542 wantedBy = [ "sockets.target" ];
543 listenStreams = [ "/run/${dirName}/virtlockd-sock" ];
546 systemd.services.virtlockd = {
547 description = "Virtual machine lock manager";
548 serviceConfig.ExecStart = "@${cfg.package}/sbin/virtlockd virtlockd";
549 restartIfChanged = false;
552 # https://libvirt.org/daemons.html#monolithic-systemd-integration
553 systemd.sockets.libvirtd.wantedBy = [ "sockets.target" ];
555 systemd.tmpfiles.rules = let
556 vhostUserCollection = pkgs.buildEnv {
558 paths = cfg.qemu.vhostUserPackages;
559 pathsToLink = [ "/share/qemu/vhost-user" ];
562 "L+ /var/lib/qemu/vhost-user - - - - ${vhostUserCollection}/share/qemu/vhost-user"
563 "L+ /var/lib/qemu/firmware - - - - ${cfg.qemu.package}/share/qemu/firmware"
569 polkit.addRule(function(action, subject) {
570 if (action.id == "org.libvirt.unix.manage" &&
571 subject.isInGroup("libvirtd")) {
572 return polkit.Result.YES;
578 system.nssModules = optional (cfg.nss.enable or cfg.nss.enableGuest) cfg.package;
579 system.nssDatabases.hosts = mkMerge [
580 # ensure that the NSS modules come between mymachines (which is 400) and resolve (which is 501)
581 (mkIf cfg.nss.enable (mkOrder 430 [ "libvirt" ]))
582 (mkIf cfg.nss.enableGuest (mkOrder 432 [ "libvirt_guest" ]))