2023-10-20 06:10:59 +02:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
|
|
|
|
let
|
|
|
|
cfg = config.virtualisation.incus;
|
|
|
|
preseedFormat = pkgs.formats.yaml { };
|
|
|
|
in
|
|
|
|
{
|
2023-12-02 15:38:44 +01:00
|
|
|
meta = {
|
|
|
|
maintainers = lib.teams.lxc.members;
|
|
|
|
};
|
2023-10-20 06:10:59 +02:00
|
|
|
|
|
|
|
options = {
|
|
|
|
virtualisation.incus = {
|
|
|
|
enable = lib.mkEnableOption (lib.mdDoc ''
|
|
|
|
incusd, a daemon that manages containers and virtual machines.
|
|
|
|
|
|
|
|
Users in the "incus-admin" group can interact with
|
|
|
|
the daemon (e.g. to start or stop containers) using the
|
|
|
|
{command}`incus` command line tool, among others.
|
|
|
|
'');
|
|
|
|
|
2023-11-30 19:03:14 +01:00
|
|
|
package = lib.mkPackageOption pkgs "incus" { };
|
2023-10-20 06:10:59 +02:00
|
|
|
|
2023-11-30 19:03:14 +01:00
|
|
|
lxcPackage = lib.mkPackageOption pkgs "lxc" { };
|
2023-10-20 06:10:59 +02:00
|
|
|
|
|
|
|
preseed = lib.mkOption {
|
|
|
|
type = lib.types.nullOr (
|
|
|
|
lib.types.submodule { freeformType = preseedFormat.type; }
|
|
|
|
);
|
|
|
|
|
|
|
|
default = null;
|
|
|
|
|
|
|
|
description = lib.mdDoc ''
|
|
|
|
Configuration for Incus preseed, see
|
|
|
|
<https://linuxcontainers.org/incus/docs/main/howto/initialize/#non-interactive-configuration>
|
|
|
|
for supported values.
|
|
|
|
|
|
|
|
Changes to this will be re-applied to Incus which will overwrite existing entities or create missing ones,
|
|
|
|
but entities will *not* be removed by preseed.
|
|
|
|
'';
|
|
|
|
|
|
|
|
example = {
|
|
|
|
networks = [
|
|
|
|
{
|
|
|
|
name = "incusbr0";
|
|
|
|
type = "bridge";
|
|
|
|
config = {
|
|
|
|
"ipv4.address" = "10.0.100.1/24";
|
|
|
|
"ipv4.nat" = "true";
|
|
|
|
};
|
|
|
|
}
|
|
|
|
];
|
|
|
|
profiles = [
|
|
|
|
{
|
|
|
|
name = "default";
|
|
|
|
devices = {
|
|
|
|
eth0 = {
|
|
|
|
name = "eth0";
|
|
|
|
network = "incusbr0";
|
|
|
|
type = "nic";
|
|
|
|
};
|
|
|
|
root = {
|
|
|
|
path = "/";
|
|
|
|
pool = "default";
|
|
|
|
size = "35GiB";
|
|
|
|
type = "disk";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
];
|
|
|
|
storage_pools = [
|
|
|
|
{
|
|
|
|
name = "default";
|
|
|
|
driver = "dir";
|
|
|
|
config = {
|
|
|
|
source = "/var/lib/incus/storage-pools/default";
|
|
|
|
};
|
|
|
|
}
|
|
|
|
];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
socketActivation = lib.mkEnableOption (
|
|
|
|
lib.mdDoc ''
|
|
|
|
socket-activation for starting incus.service. Enabling this option
|
|
|
|
will stop incus.service from starting automatically on boot.
|
|
|
|
''
|
|
|
|
);
|
|
|
|
|
|
|
|
startTimeout = lib.mkOption {
|
|
|
|
type = lib.types.ints.unsigned;
|
|
|
|
default = 600;
|
|
|
|
apply = toString;
|
|
|
|
description = lib.mdDoc ''
|
|
|
|
Time to wait (in seconds) for incusd to become ready to process requests.
|
|
|
|
If incusd does not reply within the configured time, `incus.service` will be
|
|
|
|
considered failed and systemd will attempt to restart it.
|
|
|
|
'';
|
|
|
|
};
|
2024-02-16 13:59:23 +01:00
|
|
|
|
|
|
|
ui = {
|
|
|
|
enable = lib.mkEnableOption (lib.mdDoc "(experimental) Incus UI");
|
|
|
|
|
|
|
|
package = lib.mkPackageOption pkgs [ "incus" "ui" ] { };
|
|
|
|
};
|
2023-10-20 06:10:59 +02:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = lib.mkIf cfg.enable {
|
2024-02-23 21:39:53 +01:00
|
|
|
assertions = [
|
|
|
|
{
|
|
|
|
assertion = !(config.networking.firewall.enable && !config.networking.nftables.enable && config.virtualisation.incus.enable);
|
|
|
|
message = "Incus on NixOS is unsupported using iptables. Set `networking.nftables.enable = true;`";
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
2023-10-20 06:10:59 +02:00
|
|
|
# https://github.com/lxc/incus/blob/f145309929f849b9951658ad2ba3b8f10cbe69d1/doc/reference/server_settings.md
|
|
|
|
boot.kernel.sysctl = {
|
|
|
|
"fs.aio-max-nr" = lib.mkDefault 524288;
|
|
|
|
"fs.inotify.max_queued_events" = lib.mkDefault 1048576;
|
|
|
|
"fs.inotify.max_user_instances" = lib.mkOverride 1050 1048576; # override in case conflict nixos/modules/services/x11/xserver.nix
|
|
|
|
"fs.inotify.max_user_watches" = lib.mkOverride 1050 1048576; # override in case conflict nixos/modules/services/x11/xserver.nix
|
|
|
|
"kernel.dmesg_restrict" = lib.mkDefault 1;
|
|
|
|
"kernel.keys.maxbytes" = lib.mkDefault 2000000;
|
|
|
|
"kernel.keys.maxkeys" = lib.mkDefault 2000;
|
|
|
|
"net.core.bpf_jit_limit" = lib.mkDefault 1000000000;
|
|
|
|
"net.ipv4.neigh.default.gc_thresh3" = lib.mkDefault 8192;
|
|
|
|
"net.ipv6.neigh.default.gc_thresh3" = lib.mkDefault 8192;
|
|
|
|
# vm.max_map_count is set higher in nixos/modules/config/sysctl.nix
|
|
|
|
};
|
|
|
|
|
|
|
|
boot.kernelModules = [
|
|
|
|
"veth"
|
|
|
|
"xt_comment"
|
|
|
|
"xt_CHECKSUM"
|
|
|
|
"xt_MASQUERADE"
|
|
|
|
"vhost_vsock"
|
|
|
|
] ++ lib.optionals (!config.networking.nftables.enable) [ "iptable_mangle" ];
|
|
|
|
|
|
|
|
environment.systemPackages = [ cfg.package ];
|
|
|
|
|
|
|
|
# Note: the following options are also declared in virtualisation.lxc, but
|
|
|
|
# the latter can't be simply enabled to reuse the formers, because it
|
|
|
|
# does a bunch of unrelated things.
|
|
|
|
systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ];
|
|
|
|
|
|
|
|
security.apparmor = {
|
|
|
|
packages = [ cfg.lxcPackage ];
|
|
|
|
policies = {
|
|
|
|
"bin.lxc-start".profile = ''
|
|
|
|
include ${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start
|
|
|
|
'';
|
|
|
|
"lxc-containers".profile = ''
|
|
|
|
include ${cfg.lxcPackage}/etc/apparmor.d/lxc-containers
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
systemd.services.incus = {
|
|
|
|
description = "Incus Container and Virtual Machine Management Daemon";
|
|
|
|
|
|
|
|
wantedBy = lib.mkIf (!cfg.socketActivation) [ "multi-user.target" ];
|
|
|
|
after = [
|
|
|
|
"network-online.target"
|
|
|
|
"lxcfs.service"
|
2024-01-04 20:29:36 +01:00
|
|
|
"incus.socket"
|
|
|
|
];
|
2023-10-20 06:10:59 +02:00
|
|
|
requires = [
|
|
|
|
"lxcfs.service"
|
2024-01-04 20:29:36 +01:00
|
|
|
"incus.socket"
|
|
|
|
];
|
2023-10-20 06:10:59 +02:00
|
|
|
wants = [
|
|
|
|
"network-online.target"
|
|
|
|
];
|
|
|
|
|
2024-01-23 18:51:18 +01:00
|
|
|
path = lib.mkIf config.boot.zfs.enabled [
|
|
|
|
config.boot.zfs.package
|
|
|
|
"${config.boot.zfs.package}/lib/udev"
|
|
|
|
];
|
2023-10-20 06:10:59 +02:00
|
|
|
|
2024-02-16 13:59:23 +01:00
|
|
|
environment = lib.mkMerge [ {
|
2023-10-20 06:10:59 +02:00
|
|
|
# Override Path to the LXC template configuration directory
|
|
|
|
INCUS_LXC_TEMPLATE_CONFIG = "${pkgs.lxcfs}/share/lxc/config";
|
2024-02-16 13:59:23 +01:00
|
|
|
} (lib.mkIf (cfg.ui.enable) {
|
|
|
|
"INCUS_UI" = cfg.ui.package;
|
|
|
|
}) ];
|
2023-10-20 06:10:59 +02:00
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
ExecStart = "${cfg.package}/bin/incusd --group incus-admin";
|
|
|
|
ExecStartPost = "${cfg.package}/bin/incusd waitready --timeout=${cfg.startTimeout}";
|
2023-10-31 13:34:57 +01:00
|
|
|
ExecStop = "${cfg.package}/bin/incus admin shutdown";
|
2023-10-20 06:10:59 +02:00
|
|
|
|
|
|
|
KillMode = "process"; # when stopping, leave the containers alone
|
|
|
|
Delegate = "yes";
|
|
|
|
LimitMEMLOCK = "infinity";
|
|
|
|
LimitNOFILE = "1048576";
|
|
|
|
LimitNPROC = "infinity";
|
|
|
|
TasksMax = "infinity";
|
|
|
|
|
|
|
|
Restart = "on-failure";
|
|
|
|
TimeoutStartSec = "${cfg.startTimeout}s";
|
|
|
|
TimeoutStopSec = "30s";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2024-01-04 20:29:36 +01:00
|
|
|
systemd.sockets.incus = {
|
2023-10-20 06:10:59 +02:00
|
|
|
description = "Incus UNIX socket";
|
|
|
|
wantedBy = [ "sockets.target" ];
|
|
|
|
|
|
|
|
socketConfig = {
|
|
|
|
ListenStream = "/var/lib/incus/unix.socket";
|
|
|
|
SocketMode = "0660";
|
|
|
|
SocketGroup = "incus-admin";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
systemd.services.incus-preseed = lib.mkIf (cfg.preseed != null) {
|
|
|
|
description = "Incus initialization with preseed file";
|
2023-10-28 00:05:58 +02:00
|
|
|
|
|
|
|
wantedBy = ["incus.service"];
|
|
|
|
after = ["incus.service"];
|
|
|
|
bindsTo = ["incus.service"];
|
|
|
|
partOf = ["incus.service"];
|
2023-10-20 06:10:59 +02:00
|
|
|
|
|
|
|
script = ''
|
|
|
|
${cfg.package}/bin/incus admin init --preseed <${
|
|
|
|
preseedFormat.generate "incus-preseed.yaml" cfg.preseed
|
|
|
|
}
|
|
|
|
'';
|
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
Type = "oneshot";
|
2023-10-28 00:05:58 +02:00
|
|
|
RemainAfterExit = true;
|
2023-10-20 06:10:59 +02:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
users.groups.incus-admin = { };
|
|
|
|
|
|
|
|
users.users.root = {
|
|
|
|
# match documented default ranges https://linuxcontainers.org/incus/docs/main/userns-idmap/#allowed-ranges
|
|
|
|
subUidRanges = [
|
|
|
|
{
|
|
|
|
startUid = 1000000;
|
|
|
|
count = 1000000000;
|
|
|
|
}
|
|
|
|
];
|
|
|
|
subGidRanges = [
|
|
|
|
{
|
|
|
|
startGid = 1000000;
|
|
|
|
count = 1000000000;
|
|
|
|
}
|
|
|
|
];
|
|
|
|
};
|
|
|
|
|
|
|
|
virtualisation.lxc.lxcfs.enable = true;
|
|
|
|
};
|
|
|
|
}
|