Incus, a manager and hypervisor for system containers (LXC) and virtual machines (QEMU), is an excellent tool for managing and orchestrating your applications and services. It is a fork of LXD by the original maintainers.

I found the documentation regarding NixOS lacking and thought I should put it somewhere for future reference. If you have experience with LXD, it will mostly be similar but expect things to get different as time passes.

Installation

Incus is already present in nixpkgs and can be installed by adding

1
virtualisation.incus.enable = true

to your configuration.nix. Consider adding yourself to incus-admin group to avoid using sudo every time. It can be done by

1
users.user.USER.extraGroups = [ "incus-admin" ];

Of course, replace USER with your username.

You need IP forwarding for NAT’ing to work

1
2
3
4
boot.kernel.sysctl = {
    "net.ipv4.conf.all.forwarding" = true;
    "net.ipv4.default.forwarding" = true;
};

Enable kernel module for IP forwarding to work

1
boot.kernelModules = [ "nf_nat_ftp" ];

Set up a bridge

1
networking.bridges = { incusbr0.interfaces = []; };

This is used to provide NAT’d internet to the guest. It is manipulated directly by incus, so no need to specify any bridged interfaces here.

Add firewall rules to enable networking in the container

1
2
3
4
5
6
networking.firewall.extraCommands = ''
    iptables -A INPUT incusbr0 -j ACCEPT
    iptables -A FORWARD -o incusbr0 -j ACCEPT
    iptables -A FORWARD -i incusbr0 -j ACCEPT
    iptables -A OUTPUT -o incusbr0 -j ACCEPT
'';

Incus on NixOS dropped iptables support and recommends using nftables. Enable nftables and add incusbr0 to trusted interfaces.

1
2
networking.nftables.enable = true;
networking.firewall.trustedInterfaces = [ "incusbr0" ];

Enable lxcfs to use it

1
virtualisation.lxc.lxcfs.enable = true;

Now switch to the new configuration with

1
nixos-rebuild switch

Setting up incus

Incus requires initial setup for networking and storage. It can be done interactively by running

1
incus admin init

List all available images

1
incus image list images:

Create a new image alpine based on Alpine Linux

1
incus launch images:alpine/3.19 alpine

Interact with the newly created image

1
incus exec alpine -- ash

This will drop you in an ash shell in the container.

You can copy containers by running

1
incus copy $CONTAINER1 $CONTAINER2

List containers

1
incus list

Stop container

1
incus stop $CONTAINER

Delete container

1
incus delete $CONTAINER

Configuration

Launch a new container with resource constrants

1
incus launch images:alpine/3.19 alp1 --config limits.cpu=1 --config limits.memory=192MiB

Check configuration

1
incus config show alp1

Update configuration

1
incus config set alp1 limits.memory=128MiB

Interaction

Run arbitrary commands

1
incus exec alpine -- apk update

Pull a file from container

1
incus file pull alpine/etc/hosts .

Push file back to the container

1
incus file push hosts alpine/etc/hosts

Snapshots

Create a snapshot

1
incus snapshot create alpine alpine_snapshot

Restore the container to the snapshot

1
incus snapshot restore alpine alpine_snapshot

Delete the snapshot

1
incus delete alpine/alpine_snapshot

References

  1. Howto setup LXD on NixOS with NixOS guest using unmanaged bridge network interface
  2. First steps with Incus