NixOS: Enabling LXD virtual machines with Flakes

Nixpkgs is an ever-growing collection of software packages for Nix and NixOS. Even with more than 80,000 packages, you easily run into a situation where there is a functionality that is not yet implemented.

Earlier this year we wrote one tutorial on how to implement your own package in NixOS. The implementation of the package in the system was cumbersome and required that one had control over the package one wanted to implement.

Let's dive into a practical example: LXD is one container and virtual machine manager. It supports virtual machines natively since LXD 4.0. Though, running on NixOS breaks the featureand we must find solutions to use it. To allow LXD to launch virtual machines on NixOS, we need a fix, and we want it to be both reproducible and portable.

To do that, we use lxd-agent feature that effectively solves the aforementioned problem. It was released astridyu, a contributor to nixpkgs. It has not yet been implemented in the Master branch of nixpkgs and is, as of 04/19/2022, a pull request.

Our goal is to integrate pull request into our nixpkgs to be able to start virtual machines. For this we will use Flakes. It's an experimental feature in the Nix package manager that allows reproducibility in the expansion of dependencies. We use it to mount different nixpkgs together. It is a property called composability.

If the pull request has been merged, this tutorial becomes irrelevant. To get the feature, update your Nix or NixOS to the current version of the master branch.


Step 1 – Install ZFS

ZFS is one file system which we use to generate storage pools in LXD. It is the only file system currently supported by this method.

ZFS must be installed on the NixOS machine before installing LXD. To do so, go to your configuration.nix file contained in /etc/nixos/ and add the following lines to your configuration.

boot = {        
  initrd.supportedFilesystems = [ "zfs" ];        
  supportedFilesystems = [ "zfs" ];         
  zfs.requestEncryptionCredentials = true;  


  kernelParams= [ "nohibernate" ];

services.zfs = {   
  autoScrub.enable = true;                  
  autoSnapshot.enable = true;               

In the file, the networking.hostId property must be set. It's a little tricky because it only accepts 32bit IDs that you generate on the terminal with head -c 8 /etc/machine-id.

With this ID, go to your configuration file and set it.

networking = {
  hostId = "";

Now rebuild the gear unit. This is achieved by doing sudo nixos-rebuild switch.

If the build fails because you already have LXD installed, see this section of the article.

Step 2 – Install flakes

To install Flakessimply put, in configuration.nix:

nix = {
  package = pkgs.nixFlakes; 
  extraOptions = ''experimental-features = nix-command flakes'';

You need to rebuild your switch drive again.

Getting started with Flakes

We will take advantage of Flake's power to patch our system by combining different versions of nixpkgs. The versions we will use are:

You can track the pull request with its number: #166858.

Then we proceed to override the LXD package from nixpkg's master branch version with astridyu's nixpkg's lxd-vms branch version to get lxd-agent feature applied to LXD.

Using Flakes requires you to create one flake.nix file in /etc/nixos. This is done by doing sudo nano /etc/nixos/flake.nix.

In this file we describe which nixpkgs repositories we want to add to our configuration, and how to merge them.

Note: Make sure your host namedefined below networking.hostName field in your configuration. It is a requirement.

  description = "NixOS Configuration of LXD";

  inputs = {
    nixpkgs.url = "nixpkgs/master";

    nixpkgs-lxdvm = {
      url = "github:astridyu/nixpkgs/lxd-vms";

  outputs = { self, nixpkgs, nixpkgs-lxdvm }:
      system = "x86_64-linux";
      overlay-lxdvm = final: prev: {
         lxdvm = import nixpkgs-lxdvm {
           inherit system;
           config.allowUnfree = true;
    in {
      nixosConfigurations."" = nixpkgs.lib.nixosSystem {
        inherit system;
        modules = [
          ({ config, pkgs, ... }: { nixpkgs.overlays = [ overlay-lxdvm ]; })

Installing LXD

We need to modify ours configuration.nix to enable LXD virtualization.

boot.kernelModules = ["vhost_vsock"];

virtualisation = {
  lxd = {

    package = pkgs.lxdvm.lxd.override {useQemu = true;};


We are rebuilding the gear operation. It is required that you use --impure option when rebuilding if requested by the system.

The reason for this is that Flakes is run in pure evaluation mode which is under-documented. Flakes prohibits the use of absolute paths, which can make the evaluation impure. This can happen because we are running unsupported packages.

Getting started with LXD and virtual machines

To get started with LXD, we recommend that you initialize a storage pool. This is done by sudo lxd init.

Afterwards, questions are asked regarding the first storage pool that LXD creates. We recommend this configuration for the first use.

Would you like to use LXD clustering? (yes/no) [default=no]: no
Do you want to configure a new storage pool? (yes/no) [default=yes]: yes

Name of the new storage pool [default=default]: test-storage

Name of the storage backend to use (btrfs, dir, zfs) [default=zfs]: zfs

Create a new ZFS pool? (yes/no) [default=yes]:

Would you like to use an existing empty block device (e.g. a disk or partition)? (yes/no) [default=no]:

Size in GB of the new loop device (1GB minimum) [default=30GB]:

Would you like to connect to a MAAS server? (yes/no) [default=no]:

Would you like to create a new local network bridge? (yes/no) [default=yes]:

What should the new bridge be called? [default=lxdbr0]: test-bridge

What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:

What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:

Would you like the LXD server to be available over the network? (yes/no) [default=no]:

Would you like stale cached images to be updated automatically? (yes/no) [default=yes]:

Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]:

When there is no response, the default is assumed.

Once the storage pool is configured, new virtual machines can be initialized with lxc launch --vm -s . It automatically downloads the image of your virtual machine and sets one up.

To control your virtual machines, do lxc ls.

common questions

If the rebuild fails, it's because LXD is already installed. This leads to nixos-rebuild switch fails because an LXD storage pool already exists. This means that volumes are still mounted and you need to remove them as it is necessary to rebuild LXD from scratch.

Start by recreating yours hardware-configuration.nix by doing nixos-generate-config.

This file mentions the path to your mounted volumes, and you need to unmount and remove them as follows:

  • sudo umount -v /var/lib/lxd/storage-pools/ to dismantle the storage pool.
  • sudo rm -r /var/lib/lxd to delete the entire LXD folder.

Rebuilding your system is necessary again.


Congratulations! You can now boot virtual machines with LXD on NixOS, and you learned how to use flakes to create an overlay for your nixpkgs.

This method will be deprecated when the pull request is merged, but it remains relevant if you want to integrate features that are not in the master branch.

#NixOS #Enabling #LXD #virtual #machines #Flakes

Source link

Leave a Reply