{ config, lib, pkgs, serverCfg }: let mkRouterName = { subdomain, subpath ? null }: if subpath != null then "${subdomain}-${lib.strings.sanitizeDerivationName subpath}" else subdomain; getOr = attrs: path: default: lib.attrByPath path default attrs; mkTmpfsOption = size: "--tmpfs=/tmp:rw,noexec,nosuid,size=${size}"; mkAuthentikLabels = { subdomain , subpath ? null , routerName ? mkRouterName { inherit subdomain subpath; } , middleware ? "authentik" }: lib.optionalAttrs (serverCfg.containers ? authentik) { "traefik.http.routers.${routerName}.middlewares" = middleware; }; contBuilder = { image ? null, imageStream ? null, imageFile ? null , secret ? null , subdomain ? null, subpath?null, port ? null , authentik ? false , tmpfs ? false , tmpfsSize ? "512m" , extraEnv ? { }, extraLabels ? { }, extraOptions ? [ ] , overrides ? { } }: let routerName = mkRouterName { inherit subdomain subpath; }; base = { image = if imageStream != null then "${imageStream.imageName}:${imageStream.imageTag}" else if imageFile != null then "${imageFile.imageName}:${imageFile.imageTag}" else image; imageStream = imageStream; imageFile = imageFile; environmentFiles = if secret!=null then [ config.sops.secrets."${lib.toUpper secret}".path ] else []; environment = { TZ = config.time.timeZone; } // extraEnv; labels = (if subdomain!=null then ({ "traefik.enable" = "true"; "traefik.http.routers.${routerName}.entrypoints" = "web-secure"; "traefik.http.routers.${routerName}.rule" = if subpath != null then "Host(`${subdomain}.${serverCfg.domain}`) && PathPrefix(`/${subpath}`)" else "Host(`${subdomain}.${serverCfg.domain}`)"; "traefik.http.routers.${routerName}.tls" = "true"; } // lib.optionalAttrs (port!=null) { "traefik.http.services.${routerName}.loadbalancer.server.port" = toString port; }) else { "traefik.enable" = "false"; }) // lib.optionalAttrs authentik (mkAuthentikLabels { inherit subdomain subpath routerName; }) // extraLabels; extraOptions = [ "--add-host=host.containers.internal:host-gateway" ] ++ lib.optional tmpfs (mkTmpfsOption tmpfsSize) ++ extraOptions; }; in lib.recursiveUpdate base overrides; vmBuilder = { name, vm }: ((import "${pkgs.path}/nixos/lib/eval-config.nix" { system = "x86_64-linux"; modules = [ vm.cfg ({ config, lib, modulesPath, ... }: { imports = [ "${modulesPath}/profiles/qemu-guest.nix" "${modulesPath}/virtualisation/qemu-vm.nix" ]; networking.hostName = name; networking.useDHCP = true; networking.firewall.enable = false; services.qemuGuest.enable = true; system.stateVersion = "26.05"; virtualisation = { memorySize = vm.memory or 2048; cores = vm.cores or 2; forwardPorts = let parsePortString = port: { from = "host"; host.port = port; guest.port = port; }; in if (vm ? portForward && vm.portForward != null) then map parsePortString vm.portForward else []; };}) ]; }).config.system.build.vm); in { mkContainer = contBuilder; mkVm = vmBuilder; mkApp = name: app: { inherit name; requires = { secrets = getOr app [ "requires" "secrets" ] [ ]; databases = getOr app [ "requires" "databases" ] [ ]; }; exports = { authentik = { blueprints = getOr app [ "exports" "authentik" "blueprints" ] [ ]; }; }; runtime = { paths = getOr app [ "runtime" "paths" ] [ ]; containers = getOr app [ "runtime" "containers" ] { }; vm = getOr app [ "runtime" "vm" ] null; cron = getOr app [ "runtime" "cron" ] [ ]; setup = { trigger = ""; script = null; envFile = [ ]; } // getOr app [ "runtime" "setup" ] { }; }; }; mkData = { name, dir, vars?{} }: pkgs.runCommand name vars '' mkdir -p $out cp -r ${./data + "/${dir}"}/. $out/ find $out -type f | while read file; do ${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: '' substituteInPlace "$file" --replace "@${n}@" "${toString v}" '') vars)} done ''; host = "host.containers.internal"; hostIp = if (config.virtualisation.podman.defaultNetwork.settings ? subnets) then (builtins.elemAt config.virtualisation.podman.defaultNetwork.settings.subnets 0).gateway else "10.88.0.1"; }