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 = '' 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> 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 <code> 30 virtualisation.lxc.lxcfs.enable = true; 31 </code> 32 ''; 33 }; 34 35 package = mkOption { 36 type = types.package; 37 default = pkgs.lxd; 38 defaultText = literalExpression "pkgs.lxd"; 39 description = '' 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 = '' 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 = '' 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 = '' 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 = '' 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 = [ "network-online.target" "lxcfs.service" ]; 133 requires = [ "network-online.target" "lxd.socket" "lxcfs.service" ]; 134 documentation = [ "man:lxd(1)" ]; 135 136 path = optional cfg.zfsSupport config.boot.zfs.package; 137 138 serviceConfig = { 139 ExecStart = "@${cfg.package}/bin/lxd lxd --group lxd"; 140 ExecStartPost = "${cfg.package}/bin/lxd waitready --timeout=${cfg.startTimeout}"; 141 ExecStop = "${cfg.package}/bin/lxd shutdown"; 142 143 KillMode = "process"; # when stopping, leave the containers alone 144 LimitMEMLOCK = "infinity"; 145 LimitNOFILE = "1048576"; 146 LimitNPROC = "infinity"; 147 TasksMax = "infinity"; 148 149 Restart = "on-failure"; 150 TimeoutStartSec = "${cfg.startTimeout}s"; 151 TimeoutStopSec = "30s"; 152 153 # By default, `lxd` loads configuration files from hard-coded 154 # `/usr/share/lxc/config` - since this is a no-go for us, we have to 155 # explicitly tell it where the actual configuration files are 156 Environment = mkIf (config.virtualisation.lxc.lxcfs.enable) 157 "LXD_LXC_TEMPLATE_CONFIG=${pkgs.lxcfs}/share/lxc/config"; 158 }; 159 }; 160 161 users.groups.lxd = {}; 162 163 users.users.root = { 164 subUidRanges = [ { startUid = 1000000; count = 65536; } ]; 165 subGidRanges = [ { startGid = 1000000; count = 65536; } ]; 166 }; 167 168 boot.kernel.sysctl = mkIf cfg.recommendedSysctlSettings { 169 "fs.inotify.max_queued_events" = 1048576; 170 "fs.inotify.max_user_instances" = 1048576; 171 "fs.inotify.max_user_watches" = 1048576; 172 "vm.max_map_count" = 262144; 173 "kernel.dmesg_restrict" = 1; 174 "net.ipv4.neigh.default.gc_thresh3" = 8192; 175 "net.ipv6.neigh.default.gc_thresh3" = 8192; 176 "kernel.keys.maxkeys" = 2000; 177 }; 178 179 boot.kernelModules = [ "veth" "xt_comment" "xt_CHECKSUM" "xt_MASQUERADE" ] 180 ++ optionals (!config.networking.nftables.enable) [ "iptable_mangle" ]; 181 }; 182}