{ config, lib, pkgs, ... }: with lib; let profilesDir = "/nix/var/nix/profiles/lxc"; containers = config.lxc.containers; nixPath = config.nix.nixPath; toLxcConfig' = path: a: if builtins.isString a then "${path} = ${a}\n" else if builtins.isInt a then "${path} = ${toString a}\n" 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 != {}) { environment = { systemPackages = [ pkgs.lxc pkgs.apparmor-parser lxc-rootfs ]; pathsToLink = [ "/share/lxc" ]; }; virtualisation.lxc = { enable = true; }; systemd.services = 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"; # TODO: userns? # TODO: apparmor? apparmor.profile = "generated"; environment = "TERM=linux"; }; }; config = builtins.getAttr name containers; lxcConfig = builtins.toFile "lxc-container-${name}.conf" # TODO: better 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"; serviceConfig.RemainAfterExit = true; serviceConfig.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); }; }