{ config, lib, pkgs, ... }: with lib; let cfg = config.services.podman; podman-lib = import ./podman-lib.nix { inherit lib config; }; createQuadletSource = name: containerDef: let mapHmNetworks = network: if builtins.hasAttr network cfg.networks then "podman-${network}-network.service" else null; finalConfig = let managedNetworks = if lib.isList containerDef.network then map mapHmNetworks containerDef.network else if containerDef.network != null then map mapHmNetworks [ containerDef.network ] else [ ]; in (podman-lib.deepMerge { Container = { AddCapability = containerDef.addCapabilities; AddDevice = containerDef.devices; AutoUpdate = containerDef.autoUpdate; ContainerName = name; DropCapability = containerDef.dropCapabilities; Entrypoint = containerDef.entrypoint; Environment = containerDef.environment; EnvironmentFile = containerDef.environmentFile; Exec = containerDef.exec; Group = containerDef.group; Image = containerDef.image; IP = containerDef.ip4; IP6 = containerDef.ip6; Label = (containerDef.labels // { "nix.home-manager.managed" = true; }); Network = containerDef.network; NetworkAlias = containerDef.networkAlias; PodmanArgs = containerDef.extraPodmanArgs; PublishPort = containerDef.ports; UserNS = containerDef.userNS; User = containerDef.user; Volume = containerDef.volumes; }; Install = { WantedBy = (if containerDef.autoStart then [ "default.target" "multi-user.target" ] else [ ]); }; Service = { Environment = { PATH = (builtins.concatStringsSep ":" [ "/run/wrappers/bin" "/run/current-system/sw/bin" "${config.home.homeDirectory}/.nix-profile/bin" ]); }; Restart = "always"; TimeoutStopSec = 30; }; Unit = { After = [ "network.target" ] ++ managedNetworks; Requires = managedNetworks; Description = (if (builtins.isString containerDef.description) then containerDef.description else "Service for container ${name}"); }; } containerDef.extraConfig); in '' # Automatically generated by home-manager podman container configuration # DO NOT EDIT THIS FILE DIRECTLY # # ${name}.container ${podman-lib.toQuadletIni finalConfig} ''; toQuadletInternal = name: containerDef: { assertions = podman-lib.buildConfigAsserts name containerDef.extraConfig; resourceType = "container"; serviceName = "podman-${name}"; # quadlet service name: 'podman-.service' source = podman-lib.removeBlankLines (createQuadletSource name containerDef); }; # Define the container user type as the user interface containerDefinitionType = types.submodule { options = { addCapabilities = mkOption { type = with types; listOf str; default = [ ]; example = [ "CAP_DAC_OVERRIDE" "CAP_IPC_OWNER" ]; description = "The capabilities to add to the container."; }; autoStart = mkOption { type = types.bool; default = true; description = '' Whether to start the container on boot (requires user lingering). ''; }; autoUpdate = mkOption { type = types.enum [ null "registry" "local" ]; default = null; example = "registry"; description = "The autoupdate policy for the container."; }; description = mkOption { type = with types; nullOr str; default = null; example = "My Container"; description = "The description of the container."; }; devices = mkOption { type = with types; listOf str; default = [ ]; example = [ "/dev/:/dev/" ]; description = "The devices to mount into the container"; }; dropCapabilities = mkOption { type = with types; listOf str; default = [ ]; example = [ "CAP_DAC_OVERRIDE" "CAP_IPC_OWNER" ]; description = "The capabilities to drop from the container."; }; entrypoint = mkOption { type = with types; nullOr str; default = null; example = "/foo.sh"; description = "The container entrypoint."; }; environment = mkOption { type = podman-lib.primitiveAttrs; default = { }; example = literalExpression '' { VAR1 = "0:100"; VAR2 = true; VAR3 = 5; } ''; description = "Environment variables to set in the container."; }; environmentFile = mkOption { type = with types; listOf str; default = [ ]; example = [ "/etc/environment" "/etc/other-env" ]; description = '' Paths to files containing container environment variables. ''; }; exec = mkOption { type = with types; nullOr str; default = null; example = "sleep inf"; description = "The command to run after the container start."; }; extraPodmanArgs = mkOption { type = types.listOf types.str; default = [ ]; example = [ "--security-opt=no-new-privileges" "--security-opt=seccomp=unconfined" ]; description = "Extra arguments to pass to the podman run command."; }; extraConfig = mkOption { type = podman-lib.extraConfigType; default = { }; example = literalExpression '' { Container = { User = 1000; }; Service = { TimeoutStartSec = 15; }; } ''; description = '' INI sections and values to populate the Container Quadlet. ''; }; group = mkOption { type = with types; nullOr (either int str); default = null; description = "The group ID inside the container."; }; image = mkOption { type = types.str; example = "registry.access.redhat.com/ubi9-minimal:latest"; description = "The container image."; }; ip4 = mkOption { type = with types; nullOr str; default = null; description = "Set an IPv4 address for the container."; }; ip6 = mkOption { type = with types; nullOr str; default = null; description = "Set an IPv6 address for the container."; }; labels = mkOption { type = with types; attrsOf str; default = { }; example = { app = "myapp"; some-label = "somelabel"; }; description = "The labels to apply to the container."; }; network = mkOption { type = with types; either str (listOf str); default = [ ]; apply = value: if isString value then [ value ] else value; example = literalMD '' `"host"` or `"bridge_network_1"` or `[ "bridge_network_1" "bridge_network_2" ]` ''; description = '' The network mode or network/s to connect the container to. Equivalent to `podman run --network=