{ config, lib, pkgs, ... }: with lib; let # TODO: move to flake nixcloud-webservices = pkgs.fetchFromGitHub { owner = "nixcloud"; repo = "nixcloud-webservices"; rev = "3a0767f0536fac811065eb87e6342f27eac085aa"; sha256 = "vC0vBu+0HchrevuWsmE7giouKnSt/q4F0TffwhuNJv8="; }; inherit (import "${nixcloud-webservices}/pkgs" { inherit pkgs; }) nixcloud; profilesDir = "/nix/var/nix/profiles/lxc"; inherit (config.lxc) containers; inherit (config.nix) nixPath; toLxcConfig' = path: a: if builtins.isString a then '' ${path} = ${a} '' else if builtins.isInt a then '' ${path} = ${toString a} '' else if builtins.isAttrs a then lib.concatMapStrings (name: let path' = if path == "" then name else "${path}.${name}"; in toLxcConfig' path' (builtins.getAttr name a)) (builtins.attrNames a) else if builtins.isList a then lib.concatMapStrings (toLxcConfig' path) a else throw "Invalid LXC config value"; toLxcConfig = toLxcConfig' ""; lxc-rootfs = pkgs.runCommand "lxc-rootfs" { } '' mkdir -p $out/share/lxc/rootfs/{dev,nix/store,proc,run,sys,tmp} ''; in { options = with types; { lxc.containers = mkOption { type = attrs; default = { }; }; }; config = mkIf (containers != { }) { virtualisation.lxc.enable = true; environment.systemPackages = [ nixcloud.container ]; virtualisation.lxc.defaultConfig = '' lxc.id_map = u 0 100000 65536 lxc.id_map = g 0 100000 65536 ''; users.users.root.subGidRanges = [{ count = 65536; startGid = 100000; }]; users.users.root.subUidRanges = [{ count = 65536; startUid = 100000; }]; systemd.services = if true then { } else builtins.foldl' (services: name: let systemDir = "/${profilesDir}/${name}/system"; lxcDefaults = { lxc = { uts.name = name; rootfs.path = "/run/current-system/sw/share/lxc/rootfs"; mount.entry = [ "${systemDir}/init /init none bind,ro 0 0" "/nix/store /nix/store none bind,ro 0 0" ]; autodev = 1; include = "/run/current-system/sw/share/lxc/config/common.conf"; apparmor.profile = "generated"; environment = "TERM=linux"; }; }; config = builtins.getAttr name containers; lxcConfig = builtins.toFile "lxc-container-${name}.conf" # TODO: more intelligent merging? (toLxcConfig (lxcDefaults // config.lxc)); builder = { description = "Build NixOS for lxc container ${name}"; wants = [ "nix-daemon.socket" ]; after = [ "nix-daemon.service" ]; path = with pkgs; [ coreutils nix ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; Environment = [ "NIX_PATH=${builtins.concatStringsSep ":" nixPath}" ]; }; script = '' mkdir -p ${profilesDir}/${name} nix-env -p ${profilesDir}/${name}/system \ -I nixos-config=${config.nixos-config} \ -f '' \ --set -A system ''; }; starter = { description = "LXC container ${name}"; requires = [ "lxc-container-${name}-builder.service" ]; after = [ "lxc-container-${name}-builder.service" ]; path = with pkgs; [ lxc apparmor-parser ]; script = '' mkdir -p /var/lib/lxc/${name} ln -fs ${lxcConfig} /var/lib/lxc/${name}/config lxc-start -F -n ${name} ''; }; in services // { "lxc-container-${name}-builder" = builder; "lxc-container-${name}" = starter; }) { } (builtins.attrNames containers); }; }