NixVirt
repository·master·Indexed 18 days ago
https://github.com/ashleyyakeley/nixvirtA Nix-based tool for the declarative management of libvirt virtual machines, networks, and storage pools. It provides NixOS and Home Manager modules, a set of XML generation APIs (lib.domain, lib.network, lib.pool, lib.volume) with pre-configured templates for Linux and Windows guests, and the virtdeclare CLI tool for idempotent control of libvirt objects.
What's inside NixVirt
- NixVirt is a tool that allows you to declare virtual machines (specifically libvirt domains) and their associated objects using Nix. This enables declarative management of virtualization resources within your NixOS or Home Manager configurations.
Enable usermode QEMU networking with qemu-bridge-helper
masterIf you are using the NixVirt Home Manager module, you are likely running QEMU in usermode (
qemu:///session). To allow usermode VMs to attach to a bridge, you must include the bridge name in thevirtualisation.libvirtd.allowedBridgeslist. This allows the setuidqemu-bridge-helperto manage the connection.Troubleshooting: If you encounter the error
'qemu-bridge-helper' is not a suitable bridge helper: No such file or directory, try killing the usermodevirtqemudprocess.Add NixVirt to your flake.nix
masterNixVirt is a Nix flake available via FlakeHub. To use it, add it as an input to your
flake.nix. It is recommended to useinputs.nixpkgs.follows = "nixpkgs"to ensure NixVirt uses the same version of Nixpkgs as your system, preventing version incompatibilities.{ inputs.NixVirt = { url = "https://flakehub.com/f/AshleyYakeley/NixVirt/*.tar.gz"; inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { self, NixVirt }: { # Use in your outputs }; }Attach a physical network interface to a bridge using systemd-networkd
masterTo allow a bridge to control your physical network card, you can configure it using
systemd-networkd. This involves:- Defining the bridge and assigning your physical interface (e.g.,
enp4s0) to it. - Disabling DHCP on the physical interface.
- Enabling DHCP on the bridge interface.
- Configuring a
systemd.network.networksentry to manage the bridge. - Adding the bridge name to
virtualisation.libvirtd.allowedBridgessoqemu-bridge-helpercan use it.
networking = { bridges.br0.interfaces = [ "enp4s0" ]; # controls your network card interfaces.br0.useDHCP = true; interfaces.enp4s0.useDHCP = false; }; systemd.network.networks."20-br0" = { matchConfig.Name = "br0"; networkConfig.DHCP = "yes"; dhcpV4Config.UseDomains = "yes"; # get .lan etc working }; virtualisation.libvirtd.allowedBridges = [ "br0" ];- Defining the bridge and assigning your physical interface (e.g.,
Configure libvirt networks via NixOS module
masterWhen using the NixOS module, you can define libvirt networks using XML templates. This is useful for creating bridges that can be used by the system-wide libvirt daemon. Note that bridges created this way cannot control your physical network card directly.
To create a network bridge using
nixvirtlib.network.templates.bridge, specify a uniqueuuidand asubnet_byte(which determines the subnet for DHCP, e.g.,75results in192.168.75.0/24).virtualisation.libvirt.connections."qemu:///system".networks = with nixvirtlib.network; [ { definition = writeXML (templates.bridge { uuid = "6bbe6459-51b6-4fa8-849e-eb0179523243"; # pick your own UUID subnet_byte = 75; # will run DHCP on the network for 192.168.75.0/24 }); active = true; } ]; virtualisation.libvirtd.allowedBridges = [ "virbr0" ];Configure Home Manager with `homeModules.default`
masterThe
homeModules.defaultoutput provides a Home Manager module for managing libvirt objects (domains, networks, pools) for a user session (e.g.,"qemu:///session").Requirement:
virtualisation.libvirtd.enablemust already be enabled in your NixOS configuration for this module to function.Configure NixOS modules with `nixosModules.default`
masterThe
nixosModules.defaultoutput provides a NixOS module to manage libvirt.Available Options:
virtualisation.libvirt.enable(bool, defaultfalse): Enables NixVirt. This also enablesvirtualisation.libvirtd.enableand setsvirtualisation.libvirtd.package.virtualisation.libvirt.package(package, defaultpackages.libvirt): The libvirt package to use. This also setsvirtualisation.libvirtd.package.virtualisation.libvirt.verbose(bool, defaultfalse): Enables an output trace of changes during activation, useful for debugging domain changes.virtualisation.libvirt.swtpm.enable(bool, defaultfalse): Enables the software TPM emulator (swtpm).virtualisation.libvirt.connections.<connection>(set): Configuration for a specific hypervisor connection URI (e.g.,"qemu:///system").
Connection Attributes:
domains(list of sets): Defines libvirt domains.definition(path): Path to the domain XML.active(bool ornull): Target state (running/stopped).restart(bool ornull): Whether to restart the domain.- Warning: Specifying this list will cause any libvirt domain not in the list to be deleted (though volumes/NVRAM/TPM state are preserved).
networks(list of sets): Defines libvirt networks.definition(path): Path to the network XML.active(bool ornull): Target state.restart(bool ornull): Whether to restart.- Warning: Specifying this list will cause any libvirt network not in the list to be deleted.
pools(list of sets): Defines libvirt storage pools.definition(path): Path to the pool XML.active(bool ornull): Target state.restart(bool ornull): Whether to restart.volumes(list of sets): Volumes to manage.present(bool, defaulttrue): Whether the volume should exist.definition(path, defaultnull): Path to volume XML.name(string, defaultnull): Volume name (required ifpresent = false).
- Warning: Specifying this list will cause any libvirt pool not in the list to be deleted (files/storage are preserved).
Note: NixOS already has
virtualisation.libvirtdoptions for the daemon itself.Create domain XML using `lib.domain` functions
masterThe
lib.domainAPI allows you to generate libvirt domain XML from Nix structures.lib.domain.getXML: Returns a string containing the domain XML.lib.domain.writeXML: Returns a path to a file containing the domain XML.lib.domain.templates: Provides pre-configured templates for common use cases.
Domain Templates
lib.domain.templates.linuxSuitable for Linux guests. Arguments:
name(required),uuid(required),memory(default{ count = 4; unit = "GiB"; }),storage_vol,backing_vol,install_vol,bridge_name(default"virbr0"),virtio_net,virtio_video,virtio_drive.lib.domain.templates.windowsSuitable for Windows 11 guests. Supports Secure Boot (via OVMF) and emulated TPM (requires
virtualisation.libvirt.swtpm.enable = true). Arguments:name(required),uuid(required),memory,storage_vol,backing_vol,install_vol,bridge_name,nvram_path(required),virtio_net,virtio_video,virtio_drive,install_virtio(adds a CDROM with VirtIO drivers).lib.domain.templates.pcandlib.domain.templates.q35Basic Intel machine templates (440FX and Q35 respectively).
# Example: Creating a Linux domain in Home Manager virtualisation.libvirt.connections."qemu:///session".domains = [ { definition = nixvirt.lib.domain.writeXML (nixvirt.lib.domain.templates.linux { name = "Penguin"; uuid = "cc7439ed-36af-4696-a6f2-1f0c4474d87e"; memory = { count = 6; unit = "GiB"; }; storage_vol = { pool = "MyPool"; volume = "Penguin.qcow2"; } backing_vol = /home/ashley/VM-Storage/Base.qcow2; }); } ];Create storage pool and volume XML
masterUse the
lib.poolandlib.volumeAPIs to manage libvirt storage.lib.pool.getXML: Returns a string containing the pool XML.lib.pool.writeXML: Returns a path to a file containing the pool XML.lib.volume.getXML: Returns a string containing the volume XML.lib.volume.writeXML: Returns a path to a file containing the volume XML.
Example Pool Structure:
lib.pool.getXML { name = "MyPool"; uuid = "650c5bbb-eebd-4cea-8a2f-36e1a75a8683"; type = "dir"; target = { path = "/home/ashley/VM-Storage/MyPool"; }; }Example Volume Structure:
lib.volume.getXML { name = "MainDisk"; capacity = { count = 20; unit = "GB"; }; }Create network XML using `lib.network` functions
masterThe
lib.networkAPI allows you to generate libvirt network XML.lib.network.getXML: Returns a string containing the network XML.lib.network.writeXML: Returns a path to a file containing the network XML.lib.network.templates.bridge: A template for a typical bridge used inqemu:///systemso thatqemu:///sessiondomains can connect to it.
lib.network.templates.bridgeArguments:name(default"default")uuid(required)bridge_name(default"virbr0")subnet_byte(required, integer 1-254): Sets the subnet to192.168.x.0/24wherexis thesubnet_byte.
# Example: Creating a bridge network in NixOS virtualisation.libvirt.connections."qemu:///system".networks = [ { definition = nixvirt.lib.network.writeXML (nixvirt.lib.network.templates.bridge { uuid = "70b08691-28dc-4b47-90a1-45bbeac9ab5a"; subnet_byte = 71; }); active = true; } ];Use the `virtdeclare` CLI tool
mastervirtdeclareis a command-line tool used to define and control libvirt objects (domains and networks) idempotently. It is the underlying tool used by the Nix modules.Usage:
usage: virtdeclare [-h] [-v] --connect URI --type {domain,network} (--define PATH | --uuid ID | --name ID) [--state {active,inactive}] [--auto]Options:
--connect URI: Connection URI (e.g.,qemu:///session).--type {domain,network}: The object type.--define PATH: Path to the XML definition file.--uuid ID: Object UUID.--name ID: Object name.--state {active,inactive}: Target state.--auto: Set autostart to match the state.-v, --verbose: Report actions to stderr.
Behavior:
- Redefining an object with a different definition will trigger a restart if the object is active (unless
--state inactiveis specified). - Deactivating a domain immediately terminates it (equivalent to pulling the power).
- Currently only supports
domainandnetworktypes.
virtdeclare --connect qemu:///session --type domain --define /path/to/domain.xml --state active