NixVirt

repository·master·Indexed 18 days ago

https://github.com/ashleyyakeley/nixvirt

A 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.

Tokens
3K
Snippets
6
Records
11
Agent score
13%

What's inside NixVirt

  1. Enable usermode QEMU networking with qemu-bridge-helper

    master

    If 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 the virtualisation.libvirtd.allowedBridges list. This allows the setuid qemu-bridge-helper to 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 usermode virtqemud process.

  2. Add NixVirt to your flake.nix

    master

    NixVirt is a Nix flake available via FlakeHub. To use it, add it as an input to your flake.nix. It is recommended to use inputs.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
      };
    }
  3. Attach a physical network interface to a bridge using systemd-networkd

    master

    To allow a bridge to control your physical network card, you can configure it using systemd-networkd. This involves:

    1. Defining the bridge and assigning your physical interface (e.g., enp4s0) to it.
    2. Disabling DHCP on the physical interface.
    3. Enabling DHCP on the bridge interface.
    4. Configuring a systemd.network.networks entry to manage the bridge.
    5. Adding the bridge name to virtualisation.libvirtd.allowedBridges so qemu-bridge-helper can 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" ];
  4. Configure libvirt networks via NixOS module

    master

    When 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 unique uuid and a subnet_byte (which determines the subnet for DHCP, e.g., 75 results in 192.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" ];
  5. Configure Home Manager with `homeModules.default`

    master

    The homeModules.default output provides a Home Manager module for managing libvirt objects (domains, networks, pools) for a user session (e.g., "qemu:///session").

    Requirement: virtualisation.libvirtd.enable must already be enabled in your NixOS configuration for this module to function.

  6. Configure NixOS modules with `nixosModules.default`

    master

    The nixosModules.default output provides a NixOS module to manage libvirt.

    Available Options:

    • virtualisation.libvirt.enable (bool, default false): Enables NixVirt. This also enables virtualisation.libvirtd.enable and sets virtualisation.libvirtd.package.
    • virtualisation.libvirt.package (package, default packages.libvirt): The libvirt package to use. This also sets virtualisation.libvirtd.package.
    • virtualisation.libvirt.verbose (bool, default false): Enables an output trace of changes during activation, useful for debugging domain changes.
    • virtualisation.libvirt.swtpm.enable (bool, default false): 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 or null): Target state (running/stopped).
      • restart (bool or null): 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 or null): Target state.
      • restart (bool or null): 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 or null): Target state.
      • restart (bool or null): Whether to restart.
      • volumes (list of sets): Volumes to manage.
        • present (bool, default true): Whether the volume should exist.
        • definition (path, default null): Path to volume XML.
        • name (string, default null): Volume name (required if present = 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.libvirtd options for the daemon itself.

  7. Create domain XML using `lib.domain` functions

    master

    The lib.domain API 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.linux

    Suitable 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.windows

    Suitable 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.pc and lib.domain.templates.q35

    Basic 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;
          });
      }
    ];
  8. Create storage pool and volume XML

    master

    Use the lib.pool and lib.volume APIs 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"; };
    }
  9. Create network XML using `lib.network` functions

    master

    The lib.network API 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 in qemu:///system so that qemu:///session domains can connect to it.

    lib.network.templates.bridge Arguments:

    • name (default "default")
    • uuid (required)
    • bridge_name (default "virbr0")
    • subnet_byte (required, integer 1-254): Sets the subnet to 192.168.x.0/24 where x is the subnet_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;
      }
    ];
  10. Use the `virtdeclare` CLI tool

    master

    virtdeclare is 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 inactive is specified).
    • Deactivating a domain immediately terminates it (equivalent to pulling the power).
    • Currently only supports domain and network types.
    virtdeclare --connect qemu:///session --type domain --define /path/to/domain.xml --state active