1# Systemd services for lxd. 2 3{ config, lib, pkgs, ... }: 4 5with lib; 6 7let 8 cfg = config.virtualisation.lxd; 9in { 10 imports = [ 11 (mkRemovedOptionModule [ "virtualisation" "lxd" "zfsPackage" ] "Override zfs in an overlay instead to override it globally") 12 ]; 13 14 ###### interface 15 16 options = { 17 virtualisation.lxd = { 18 enable = mkOption { 19 type = types.bool; 20 default = false; 21 description = lib.mdDoc '' 22 This option enables lxd, a daemon that manages 23 containers. Users in the "lxd" group can interact with 24 the daemon (e.g. to start or stop containers) using the 25 {command}`lxc` command line tool, among others. 26 27 Most of the time, you'll also want to start lxcfs, so 28 that containers can "see" the limits: 29 ``` 30 virtualisation.lxc.lxcfs.enable = true; 31 ``` 32 ''; 33 }; 34 35 package = mkOption { 36 type = types.package; 37 default = pkgs.lxd; 38 defaultText = literalExpression "pkgs.lxd"; 39 description = lib.mdDoc '' 40 The LXD package to use. 41 ''; 42 }; 43 44 lxcPackage = mkOption { 45 type = types.package; 46 default = pkgs.lxc; 47 defaultText = literalExpression "pkgs.lxc"; 48 description = lib.mdDoc '' 49 The LXC package to use with LXD (required for AppArmor profiles). 50 ''; 51 }; 52 53 zfsSupport = mkOption { 54 type = types.bool; 55 default = config.boot.zfs.enabled; 56 defaultText = literalExpression "config.boot.zfs.enabled"; 57 description = lib.mdDoc '' 58 Enables lxd to use zfs as a storage for containers. 59 60 This option is enabled by default if a zfs pool is configured 61 with nixos. 62 ''; 63 }; 64 65 recommendedSysctlSettings = mkOption { 66 type = types.bool; 67 default = false; 68 description = lib.mdDoc '' 69 Enables various settings to avoid common pitfalls when 70 running containers requiring many file operations. 71 Fixes errors like "Too many open files" or 72 "neighbour: ndisc_cache: neighbor table overflow!". 73 See https://lxd.readthedocs.io/en/latest/production-setup/ 74 for details. 75 ''; 76 }; 77 78 startTimeout = mkOption { 79 type = types.int; 80 default = 600; 81 apply = toString; 82 description = lib.mdDoc '' 83 Time to wait (in seconds) for LXD to become ready to process requests. 84 If LXD does not reply within the configured time, lxd.service will be 85 considered failed and systemd will attempt to restart it. 86 ''; 87 }; 88 }; 89 }; 90 91 ###### implementation 92 config = mkIf cfg.enable { 93 environment.systemPackages = [ cfg.package ]; 94 95 # Note: the following options are also declared in virtualisation.lxc, but 96 # the latter can't be simply enabled to reuse the formers, because it 97 # does a bunch of unrelated things. 98 systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ]; 99 100 security.apparmor = { 101 packages = [ cfg.lxcPackage ]; 102 policies = { 103 "bin.lxc-start".profile = '' 104 include ${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start 105 ''; 106 "lxc-containers".profile = '' 107 include ${cfg.lxcPackage}/etc/apparmor.d/lxc-containers 108 ''; 109 }; 110 }; 111 112 # TODO: remove once LXD gets proper support for cgroupsv2 113 # (currently most of the e.g. CPU accounting stuff doesn't work) 114 systemd.enableUnifiedCgroupHierarchy = false; 115 116 systemd.sockets.lxd = { 117 description = "LXD UNIX socket"; 118 wantedBy = [ "sockets.target" ]; 119 120 socketConfig = { 121 ListenStream = "/var/lib/lxd/unix.socket"; 122 SocketMode = "0660"; 123 SocketGroup = "lxd"; 124 Service = "lxd.service"; 125 }; 126 }; 127 128 systemd.services.lxd = { 129 description = "LXD Container Management Daemon"; 130 131 wantedBy = [ "multi-user.target" ]; 132 after = [ 133 "network-online.target" 134 (mkIf config.virtualisation.lxc.lxcfs.enable "lxcfs.service") 135 ]; 136 requires = [ 137 "network-online.target" 138 "lxd.socket" 139 (mkIf config.virtualisation.lxc.lxcfs.enable "lxcfs.service") 140 ]; 141 documentation = [ "man:lxd(1)" ]; 142 143 path = [ pkgs.util-linux ] 144 ++ optional cfg.zfsSupport config.boot.zfs.package; 145 146 serviceConfig = { 147 ExecStart = "@${cfg.package}/bin/lxd lxd --group lxd"; 148 ExecStartPost = "${cfg.package}/bin/lxd waitready --timeout=${cfg.startTimeout}"; 149 ExecStop = "${cfg.package}/bin/lxd shutdown"; 150 151 KillMode = "process"; # when stopping, leave the containers alone 152 LimitMEMLOCK = "infinity"; 153 LimitNOFILE = "1048576"; 154 LimitNPROC = "infinity"; 155 TasksMax = "infinity"; 156 157 Restart = "on-failure"; 158 TimeoutStartSec = "${cfg.startTimeout}s"; 159 TimeoutStopSec = "30s"; 160 161 # By default, `lxd` loads configuration files from hard-coded 162 # `/usr/share/lxc/config` - since this is a no-go for us, we have to 163 # explicitly tell it where the actual configuration files are 164 Environment = mkIf (config.virtualisation.lxc.lxcfs.enable) 165 "LXD_LXC_TEMPLATE_CONFIG=${pkgs.lxcfs}/share/lxc/config"; 166 }; 167 }; 168 169 users.groups.lxd = {}; 170 171 users.users.root = { 172 subUidRanges = [ { startUid = 1000000; count = 65536; } ]; 173 subGidRanges = [ { startGid = 1000000; count = 65536; } ]; 174 }; 175 176 boot.kernel.sysctl = mkIf cfg.recommendedSysctlSettings { 177 "fs.inotify.max_queued_events" = 1048576; 178 "fs.inotify.max_user_instances" = 1048576; 179 "fs.inotify.max_user_watches" = 1048576; 180 "vm.max_map_count" = 262144; 181 "kernel.dmesg_restrict" = 1; 182 "net.ipv4.neigh.default.gc_thresh3" = 8192; 183 "net.ipv6.neigh.default.gc_thresh3" = 8192; 184 "kernel.keys.maxkeys" = 2000; 185 }; 186 187 boot.kernelModules = [ "veth" "xt_comment" "xt_CHECKSUM" "xt_MASQUERADE" ] 188 ++ optionals (!config.networking.nftables.enable) [ "iptable_mangle" ]; 189 }; 190}