NixVirt lets you declare virtual machines (libvirt domains) and associated objects in Nix.
NixVirt is a Nix flake, which you can obtain from FlakeHub. (Note that the master branch on GitHub is frequently broken.)
Add NixVirt to your own flake.nix:
{
inputs.NixVirt =
{
url = "https://flakehub.com/f/AshleyYakeley/NixVirt/*.tar.gz";
inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, NixVirt }:
{
# Use in your outputs
};
}You should probably use inputs.nixpkgs.follows to keep NixVirt stuff at the same version as your system software,
to prevent various version incompatibilities.
These are the available outputs:
A NixOS module with these options:
-
virtualisation.libvirt.enable(bool, defaultfalse)
Whether to use NixVirt. Switching this on will also switch onvirtualisation.libvirtd.enable, and set by defaultvirtualisation.libvirtd.packageto match libvirt version. -
virtualisation.libvirt.swtpm.enable(bool, defaultfalse)
Whether to make swtpm (software TPM emulator) available. -
virtualisation.libvirt.connections.<connection>(set)
<connection>is the hypervisor connection URI, typically"qemu:///system". -
virtualisation.libvirt.connections.<connection>.domains(list of sets ornull, defaultnull)
Each set represents a libvirt domain, and has these attributes:-
definition(path)
Path to a domain definition XML file. You can obtain this for your existing domains withvirsh dumpxml. -
active(bool ornull, defaultnull)
State to put the domain in (running/stopped), or null to ignore.
⚠️ If this option is specified and not null, any libvirt domain not defined in the list will be deleted. Deleting a domain will not delete its volumes, NVRAM, or TPM state. -
-
virtualisation.libvirt.connections.<connection>.networks(list of sets ornull, defaultnull)
Each set represents a libvirt network, and has these attributes:-
definition(path)
Path to a network definition XML file. You can obtain this for your existing networks withvirsh net-dumpxml. -
active(bool ornull, defaultnull)
State to put the network in, or null to ignore.
⚠️ If this option is specified and not null, any libvirt network not defined in the list will be deleted. -
-
virtualisation.libvirt.connections.<connection>.pools(list of sets ornull, defaultnull)
Each set represents a libvirt storage pool, and has these attributes:-
definition(path)
Path to a pool definition XML file. You can obtain this for your existing pools withvirsh pool-dumpxml. -
active(bool ornull, defaultnull)
State to put the pool in, or null to ignore. -
volumes(list of sets, default[])
Volumes to create if not already existing. Existing volumes not listed will be ignored (not deleted); to delete a volume, setpresent = false. Each set has this attribute:-
present(bool, defaulttrue)
Whether the volume with the specified name should exist. -
definition(path, defaultnull)
Path to a volume definition XML file. -
name(string, defaultnull)
Name of volume, can be used instead ofdefinitionwhenpresent = false.
At least one of
definitionandnameshould exist (non-null); if they both do, the names must match. -
⚠️ If this option is specified and not null, any libvirt pool not defined in the list will be deleted. However, deleting a pool does not delete the files or other storage holding the volumes it contained. -
Note that NixOS already has options under virtualisation.libvirtd for controlling the libvirt daemon.
The same as above, as a Home Manager module, except that virtualisation.libvirtd.enable must already be switched on in NixOS.
You may want to use "qemu:///session" for the connection.
virtdeclare is a command-line tool for defining and controlling libvirt objects idempotently, used by the modules.
usage: virtdeclare [-h] [-v] --connect URI --type {domain,network} (--define PATH | --uuid ID | --name ID)
[--state {active,inactive}] [--auto]
Define and control libvirt objects idempotently.
options:
-h, --help show this help message and exit
-v, --verbose report actions to stderr
--connect URI connection URI (e.g. qemu:///session)
--type {domain,network}
object type
--define PATH XML object definition file path
--uuid ID object UUID
--name ID object name
--state {active,inactive}
state to put object in
--auto set autostart to match state
Currently virtdeclare only controls libvirt domains and networks.
-
An object definition will replace any previous definition with that UUID. The name of a definition can change, but libvirt will not allow two objects of the same type with the same name.
-
For domains, active means running.
-
Deactivating a domain immediately terminates it (like shutting the power off).
-
If an existing object is redefined, and the definition differs, and the object is active, and
--state inactiveis not specified, thenvirtdeclarewill deactivate and reactivate the object with the new definition.
A package containing virtdeclare.
Functions for creating libvirt object definition XML from Nix sets.
Create domain XML for a given structure (returns a string). The Nix structure roughly follows the XML format, but is currently missing elements. Please edit the file and create a PR for anything you need.
See this file.
Write domain XML for a given structure (returns a path).
Templates for domains, suitable for passing to lib.domain.writeXML.
These definitions are currently not stable, and are mostly for getting started with, so you should not depend on their precise configuration.
If you have suggestions for improvements, please create a PR.
A template function for a kinda basic Intel 440FX machine.
These are the arguments:
name: the libvirt name (string, required)uuid: the libvirt identifier (UUID string, required)memory: amount of RAM (set withcount(integer) andunit(string) attributes, default{ count = 2; unit = "GiB"; })storage_vol: source element or path to a QCOW2 volume for storage, or null (set, string or path, defaultnull)install_vol: source element or path to an ISO image for an inserted CDROM, or null (set, string or path, defaultnull)virtio_net: whether to use VirtIO for networking (faster, but may require special guest drivers) (bool, defaultfalse)virtio_video: whether to use VirtIO for graphics (bool, defaulttrue)virtio_drive: whether to use VirtIO for the storage device (bool, defaulttrue)
A template function for a kinda basic Intel Q35 machine.
These are the arguments:
name: the libvirt name (string, required)uuid: the libvirt identifier (UUID string, required)memory: amount of RAM (set withcount(integer) andunit(string) attributes, default{ count = 2; unit = "GiB"; })storage_vol: source element or path to a QCOW2 volume for storage, or null (set, string or path, defaultnull)install_vol: source element or path to an ISO image for an inserted CDROM, or null (set, string or path, defaultnull)virtio_net: whether to use VirtIO for networking (faster, but may require special guest drivers) (bool, defaultfalse)virtio_video: whether to use VirtIO for graphics (bool, defaulttrue)virtio_drive: whether to use VirtIO for the storage device (bool, defaulttrue)
A template function for a domain suitable for installing Linux on.
These are the arguments:
name: the libvirt name (string, required)uuid: the libvirt identifier (UUID string, required)memory: amount of RAM (set withcount(integer) andunit(string) attributes, default{ count = 4; unit = "GiB"; })storage_vol: source element or path to a QCOW2 volume for storage, or null (set, string or path, defaultnull)install_vol: source element or path to an ISO image for an inserted CDROM, or null (set, string or path, defaultnull)virtio_video: whether to use VirtIO for graphics (bool, defaulttrue)virtio_drive: whether to use VirtIO for the storage device (bool, defaulttrue)
In your Home Manager configuration:
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"; }
});
}
];A template function for a domain suitable for installing Windows 11 on.
It supports Secure Boot via OVMF and an emulated TPM; you will want to switch on virtualisation.libvirt.swtpm.enable.
These are the arguments:
name: the libvirt name (string, required)uuid: the libvirt identifier (UUID string, required)memory: amount of RAM (set withcount(integer) andunit(string) attributes, default{ count = 4; unit = "GiB"; })storage_vol: source element or path to a QCOW2 volume for storage, or null (set, string or path, defaultnull)install_vol: source element or path to an ISO image for an inserted CDROM, or null (set, string or path, defaultnull)nvram_path: path to a file for storing NVRAM, this file will be created if missing (string or path, required)virtio_net: whether to use VirtIO for networking: this is faster, but requires installing a driver during Windows 11 installation (bool, defaultfalse)virtio_video: whether to use VirtIO for graphics (bool, defaulttrue)virtio_drive: whether to use VirtIO for the storage device: this is faster, but requires installing a driver during Windows 11 installation (bool, defaultfalse)install_virtio: whether to add an additional CDROM drive with a disc containing VirtIO drivers for Windows (bool, defaultfalse)
In your Home Manager configuration:
virtualisation.libvirt.swtpm.enable = true;
virtualisation.libvirt.connections."qemu:///session".domains =
[
{
definition = nixvirt.lib.domain.writeXML (nixvirt.lib.domain.templates.windows
{
name = "Bellevue";
uuid = "def734bb-e2ca-44ee-80f5-0ea0f2593aaa";
memory = { count = 8; unit = "GiB"; };
storage_vol = { pool = "MyPool"; volume = "Bellevue.qcow2"; };
install_vol = /home/ashley/VM-Storage/Win11_23H2_EnglishInternational_x64v2.iso;
nvram_path = /home/ashley/VM-Storage/Bellevue.nvram;
virtio_net = true;
virtio_drive = true;
install_virtio = true;
});
}
];Create network XML for a given structure (returns a string). The Nix structure roughly follows the XML format, but is currently missing elements. Please edit the file and create a PR for anything you need.
lib.network.getXML
{
name = "default";
uuid = "c4acfd00-4597-41c7-a48e-e2302234fa89";
forward =
{
mode = "nat";
nat = { port = { start = 1024; end = 65535; }; };
};
bridge = { name = "virbr0"; };
mac = { address = "52:54:00:02:77:4b"; };
ip =
{
address = "192.168.74.1";
netmask = "255.255.255.0";
dhcp = { range = { start = "192.168.74.2"; end = "192.168.74.254"; }; };
};
}Write network XML for a given structure (returns a path).
A template function for a typical bridge to be created in qemu:///system.
Domains created in qemu:///session will be able to use it.
These are the arguments:
name: the libvirt name (string, default"default")uuid: the libvirt identifier (UUID string, required)bridge_name: the network name this bridge will create (string, default"virbr0")subnet_byte: byte of the subnet (integer in range 1-254, required). Given x, the subnet of the bridge will be 192.168.x.0/24.
In your NixOS configuration:
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;
}
];Create pool XML for a given structure (returns a string). The Nix structure roughly follows the XML format, but is currently missing elements. Please edit the file and create a PR for anything you need.
lib.pool.getXML
{
name = "MyPool";
uuid = "650c5bbb-eebd-4cea-8a2f-36e1a75a8683";
type = "dir";
target = { path = "/home/ashley/VM-Storage/MyPool"; };
}Write volume XML for a given structure (returns a path).
Create volume XML for a given structure (returns a string). The Nix structure roughly follows the XML format, but is currently missing elements. Please edit the file and create a PR for anything you need.
lib.volume.getXML
{
name = "MainDisk";
capacity = { count = 20; unit = "GB"; };
}Write volume XML for a given structure (returns a path).
Various functions for creating XML text.
ISO disc image of virtio-win, for installing paravirtualised drivers, etc., inside Windows guests.