podman: add darwin support with machine management

- restructure module from `podman-linux` to platform-agnostic `podman`
- move linux-specific implementation to `modules/services/podman/linux/`
- add darwin module with declarative machine management
- implement launchd-based watchdog for auto-starting machines
- maintains backward compatibility with existing linux functionality
This commit is contained in:
Thierry Delafontaine
2025-11-01 21:42:11 +01:00
committed by Austin Horstman
parent 297a085108
commit f4bcc1ae1c
54 changed files with 878 additions and 271 deletions

2
.github/labeler.yml vendored
View File

@@ -165,7 +165,7 @@
"containers":
- changed-files:
- any-glob-to-any-file:
- modules/services/podman-linux/**/*
- modules/services/podman/linux/**/*
- modules/programs/distrobox.nix
- modules/programs/docker-cli.nix
"desktop-ui":

View File

@@ -0,0 +1,23 @@
{ lib }:
{
assertPlatform =
module: config: pkgs: platforms:
let
modulePath = lib.splitString "." module;
isEmpty = x: x == false || x == null || x == { } || x == [ ] || x == "";
in
{
assertion =
(isEmpty (lib.attrByPath modulePath null config))
|| (lib.elem pkgs.stdenv.hostPlatform.system platforms);
message =
let
platformsStr = lib.concatStringsSep "\n" (map (p: " - ${p}") (lib.sort (a: b: a < b) platforms));
in
''
The module ${module} does not support your platform. It only supports
${platformsStr}'';
};
}

View File

@@ -0,0 +1,301 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
attrNames
concatStringsSep
filterAttrs
mapAttrs'
mkIf
mkOption
mkMerge
nameValuePair
optionalString
types
;
assertions = import ./assertions.nix { inherit lib; };
cfg = config.services.podman;
machineDefinitionType = types.submodule {
options = {
cpus = mkOption {
type = types.nullOr types.ints.positive;
default = null;
example = 2;
description = "Number of CPUs to allocate to the machine. If null, uses podman's default.";
};
diskSize = mkOption {
type = types.nullOr types.ints.positive;
default = null;
example = 200;
description = "Disk size in GB for the machine. If null, uses podman's default.";
};
image = mkOption {
type = types.nullOr types.str;
default = null;
description = "Bootable image to use for the machine. If null, uses podman's default.";
};
memory = mkOption {
type = types.nullOr types.ints.positive;
default = null;
example = 8192;
description = "Memory in MB to allocate to the machine. If null, uses podman's default.";
};
rootful = mkOption {
type = types.nullOr types.bool;
default = null;
example = true;
description = ''
Whether to run the machine in rootful mode. If null, uses podman's default.
Rootful mode runs containers as root inside the VM.
'';
};
swap = mkOption {
type = types.nullOr types.ints.positive;
default = null;
example = 2048;
description = "Swap size in MB for the machine. If null, uses podman's default.";
};
timezone = mkOption {
type = types.nullOr types.str;
default = null;
example = "UTC";
description = "Timezone to set in the machine. If null, uses podman's default.";
};
username = mkOption {
type = types.nullOr types.str;
default = null;
example = "user";
description = "Username used in the machine image. If null, uses podman's default.";
};
volumes = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"/Users:/Users"
"/private:/private"
"/var/folders:/var/folders"
];
description = ''
Volumes to mount in the machine, specified as source:target pairs.
If empty, podman will use its default volume mounts.
'';
};
autoStart = mkOption {
type = types.bool;
default = true;
example = false;
description = "Whether to automatically start this machine on login.";
};
watchdogInterval = mkOption {
type = types.ints.positive;
default = 30;
example = 60;
description = "Interval in seconds to check if the machine is running";
};
};
};
mkWatchdogScript =
name: machine:
pkgs.writeShellScript "podman-machine-watchdog-${name}" ''
set -euo pipefail
MACHINE_NAME="${name}"
INTERVAL=${toString machine.watchdogInterval}
PODMAN="${lib.getExe cfg.package}"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >&2
}
check_and_start() {
local state
state=$($PODMAN machine inspect "$MACHINE_NAME" --format '{{.State}}' 2>/dev/null || echo "unknown")
case "$state" in
running)
return 0
;;
stopped|unknown)
log "Machine '$MACHINE_NAME' is starting..."
if $PODMAN machine start "$MACHINE_NAME" 2>&1 | while IFS= read -r line; do log "$line"; done; then
log "Machine '$MACHINE_NAME' started successfully"
return 0
else
log "Failed to start machine '$MACHINE_NAME'"
return 1
fi
;;
*)
log "Machine '$MACHINE_NAME' is $state"
return 1
;;
esac
}
log "Starting watchdog for machine '$MACHINE_NAME' (check interval: ''${INTERVAL}s)"
while true; do
check_and_start || true
sleep "$INTERVAL"
done
'';
in
{
options.services.podman = {
useDefaultMachine = mkOption {
type = types.bool;
default = pkgs.stdenv.hostPlatform.isDarwin;
description = ''
Whether to create and use the default podman machine.
The default machine will be named `podman-machine-default` and configured with podmans default values.
'';
readOnly = pkgs.stdenv.hostPlatform.isLinux;
};
machines = mkOption {
type = types.attrsOf machineDefinitionType;
default = { };
description = "Declarative podman machine configurations.";
example = lib.literalExpression ''
{
"dev-machine" = {
cpus = 4;
diskSize = 100;
memory = 8192;
swap = 2048;
timezone = "UTC";
volumes = [
"/Users:/Users"
"/private:/private"
];
autoStart = true;
watchdogInterval = 30;
};
"testing" = {
cpus = 2;
diskSize = 50;
image = "ghcr.io/your-org/custom-image:latest";
memory = 4096;
username = "podman";
autoStart = false;
};
}
'';
};
};
config =
let
podmanCmd = lib.getExe cfg.package;
allMachines =
cfg.machines
// (
if cfg.useDefaultMachine then
{
"podman-machine-default" = {
cpus = null;
diskSize = null;
image = null;
memory = null;
rootful = null;
swap = null;
timezone = null;
username = null;
volumes = [ ];
autoStart = true;
watchdogInterval = 30;
};
}
else
{ }
);
autoStartMachines = filterAttrs (_name: machine: machine.autoStart) allMachines;
in
mkIf cfg.enable (mkMerge [
{
assertions = [
(assertions.assertPlatform "services.podman.useDefaultMachine" config pkgs lib.platforms.darwin)
(assertions.assertPlatform "services.podman.machines" config pkgs lib.platforms.darwin)
];
}
(mkIf pkgs.stdenv.isDarwin {
home.activation.podmanMachines =
let
mkMachineInitScript =
name: machine:
let
# Automatically mount host's container config into the VM
username = if isNull machine.username then "core" else machine.username;
configVolume = "$HOME/.config/containers:/home/${username}/.config/containers";
allVolumes = [ configVolume ] ++ machine.volumes;
in
''
if ! ${podmanCmd} machine list --format '{{.Name}}' 2>/dev/null | sed 's/\*$//' | grep -q '^${name}$'; then
echo "Creating podman machine: ${name}"
${podmanCmd} machine init ${name} \
${optionalString (machine.cpus != null) "--cpus ${toString machine.cpus}"} \
${optionalString (machine.diskSize != null) "--disk-size ${toString machine.diskSize}"} \
${optionalString (machine.image != null) "--image ${machine.image}"} \
${optionalString (machine.memory != null) "--memory ${toString machine.memory}"} \
${optionalString ((machine.rootful != null) && machine.rootful) "--rootful"} \
${optionalString (machine.swap != null) "--swap ${toString machine.swap}"} \
${optionalString (machine.timezone != null) "--timezone \"${machine.timezone}\""} \
${optionalString (machine.username != null) "--username \"${machine.username}\""} \
${concatStringsSep " " (map (v: "--volume \"${v}\"") allVolumes)}
fi
'';
in
lib.hm.dag.entryAfter [ "writeBoundary" ] ''
PATH="${cfg.package}/bin:$PATH"
${concatStringsSep "\n" (lib.mapAttrsToList mkMachineInitScript allMachines)}
MANAGED_MACHINES="${concatStringsSep " " (attrNames allMachines)}"
EXISTING_MACHINES=$(${podmanCmd} machine list --format '{{.Name}}' 2>/dev/null | sed 's/\*$//' || echo "")
for machine in $EXISTING_MACHINES; do
if [[ ! " $MANAGED_MACHINES " =~ " $machine " ]]; then
echo "Removing unmanaged podman machine: $machine"
${podmanCmd} machine stop "$machine" 2>/dev/null || true
${podmanCmd} machine rm -f "$machine"
fi
done
'';
launchd.agents = mapAttrs' (
name: machine:
nameValuePair "podman-machine-${name}" {
enable = true;
config = {
ProgramArguments = [ "${mkWatchdogScript name machine}" ];
KeepAlive = {
Crashed = true;
SuccessfulExit = false;
};
ProcessType = "Background";
RunAtLoad = true;
};
}
) autoStartMachines;
})
]);
}

View File

@@ -1,7 +1,7 @@
{
config,
pkgs,
lib,
pkgs,
...
}:
let
@@ -12,21 +12,19 @@ in
meta.maintainers = [
lib.hm.maintainers.bamhm182
lib.maintainers.n-hass
lib.maintainers.delafthi
];
imports = [
./builds.nix
./containers.nix
./images.nix
./install-quadlet.nix
./networks.nix
./services.nix
./volumes.nix
./linux/default.nix
./darwin.nix
];
options.services.podman = {
enable = lib.mkEnableOption "Podman, a daemonless container engine";
package = lib.mkPackageOption pkgs "podman" { };
settings = {
containers = lib.mkOption {
type = toml.type;
@@ -94,14 +92,14 @@ in
};
config = lib.mkIf cfg.enable {
assertions = [ (lib.hm.assertions.assertPlatform "podman" pkgs lib.platforms.linux) ];
home.packages = [ cfg.package ];
services.podman.settings.storage = {
storage.driver = lib.mkDefault "overlay";
};
services.podman.settings.storage.storage.driver = lib.mkDefault "overlay";
# Configuration files are written to `$XDG_CONFIG_HOME/.config/containers`
# On Linux: podman reads them directly from this location
# On Darwin: these files are automatically mounted into the podman machine VM
# (see darwin.nix for the volume mount configuration)
xdg.configFile = {
"containers/policy.json".source =
if cfg.settings.policy != { } then

View File

@@ -5,7 +5,13 @@
...
}:
let
inherit (lib) mkOption types;
inherit (lib)
mkIf
mkOption
mkMerge
types
;
assertions = import ../assertions.nix { inherit lib; };
cfg = config.services.podman;
@@ -182,10 +188,17 @@ in
let
buildQuadlets = lib.mapAttrsToList toQuadletInternal cfg.builds;
in
lib.mkIf cfg.enable {
services.podman.internal.quadletDefinitions = buildQuadlets;
assertions = lib.flatten (map (build: build.assertions) buildQuadlets);
mkIf cfg.enable (mkMerge [
{
assertions = [
(assertions.assertPlatform "services.podman.builds" config pkgs lib.platforms.linux)
];
}
(mkIf pkgs.stdenv.hostPlatform.isLinux {
services.podman.internal.quadletDefinitions = buildQuadlets;
assertions = lib.flatten (map (build: build.assertions) buildQuadlets);
xdg.configFile."podman/images.manifest".text = podman-lib.generateManifestText buildQuadlets;
};
xdg.configFile."podman/images.manifest".text = podman-lib.generateManifestText buildQuadlets;
})
]);
}

View File

@@ -5,7 +5,13 @@
...
}:
let
inherit (lib) mkOption types;
inherit (lib)
mkIf
mkOption
mkMerge
types
;
assertions = import ../assertions.nix { inherit lib; };
cfg = config.services.podman;
@@ -401,12 +407,19 @@ in
let
containerQuadlets = lib.mapAttrsToList toQuadletInternal cfg.containers;
in
lib.mkIf cfg.enable {
services.podman.internal.quadletDefinitions = containerQuadlets;
assertions = lib.flatten (map (container: container.assertions) containerQuadlets);
mkIf cfg.enable (mkMerge [
{
assertions = [
(assertions.assertPlatform "services.podman.containers" config pkgs lib.platforms.linux)
];
}
(mkIf pkgs.stdenv.hostPlatform.isLinux {
services.podman.internal.quadletDefinitions = containerQuadlets;
assertions = lib.flatten (map (container: container.assertions) containerQuadlets);
# manifest file
xdg.configFile."podman/containers.manifest".text =
podman-lib.generateManifestText containerQuadlets;
};
# manifest file
xdg.configFile."podman/containers.manifest".text =
podman-lib.generateManifestText containerQuadlets;
})
]);
}

View File

@@ -0,0 +1,12 @@
{
imports = [
./options.nix
./builds.nix
./containers.nix
./images.nix
./install-quadlet.nix
./networks.nix
./services.nix
./volumes.nix
];
}

View File

@@ -5,7 +5,13 @@
...
}:
let
inherit (lib) mkOption types;
inherit (lib)
mkIf
mkOption
mkMerge
types
;
assertions = import ../assertions.nix { inherit lib; };
cfg = config.services.podman;
@@ -165,8 +171,15 @@ in
let
imageQuadlets = lib.mapAttrsToList toQuadletInternal cfg.images;
in
lib.mkIf cfg.enable {
services.podman.internal.quadletDefinitions = imageQuadlets;
assertions = lib.flatten (map (image: image.assertions) imageQuadlets);
};
mkIf cfg.enable (mkMerge [
{
assertions = [
(assertions.assertPlatform "services.podman.images" config pkgs lib.platforms.linux)
];
}
(mkIf pkgs.stdenv.hostPlatform.isLinux {
services.podman.internal.quadletDefinitions = imageQuadlets;
assertions = lib.flatten (map (image: image.assertions) imageQuadlets);
})
]);
}

View File

@@ -99,7 +99,7 @@ in
{
imports = [ ./options.nix ];
config = lib.mkIf cfg.enable {
config = lib.mkIf (cfg.enable && pkgs.stdenv.hostPlatform.isLinux) {
home.file = generateSystemdFileLinks allUnitFiles;
# if the length of builtQuadlets is 0, then we don't need register the activation script

View File

@@ -5,7 +5,13 @@
...
}:
let
inherit (lib) mkOption types;
inherit (lib)
mkIf
mkOption
mkMerge
types
;
assertions = import ../assertions.nix { inherit lib; };
cfg = config.services.podman;
@@ -180,10 +186,17 @@ in
let
networkQuadlets = lib.mapAttrsToList toQuadletInternal cfg.networks;
in
lib.mkIf cfg.enable {
services.podman.internal.quadletDefinitions = networkQuadlets;
assertions = lib.flatten (map (network: network.assertions) networkQuadlets);
mkIf cfg.enable (mkMerge [
{
assertions = [
(assertions.assertPlatform "services.podman.networks" config pkgs lib.platforms.linux)
];
}
(mkIf pkgs.stdenv.hostPlatform.isLinux {
services.podman.internal.quadletDefinitions = networkQuadlets;
assertions = lib.flatten (map (network: network.assertions) networkQuadlets);
xdg.configFile."podman/networks.manifest".text = podman-lib.generateManifestText networkQuadlets;
};
xdg.configFile."podman/networks.manifest".text = podman-lib.generateManifestText networkQuadlets;
})
]);
}

View File

@@ -1,37 +1,47 @@
{ lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkEnableOption mkIf mkOption;
assertions = import ../assertions.nix { inherit lib; };
cfg = config.services.podman;
# Define the systemd service type
quadletInternalType = lib.types.submodule {
options = {
assertions = lib.mkOption {
assertions = mkOption {
type = with lib.types; listOf unspecified;
default = [ ];
internal = true;
description = "List of Nix type assertions.";
};
dependencies = lib.mkOption {
dependencies = mkOption {
type = with lib.types; listOf package;
default = [ ];
internal = true;
description = "List of systemd service dependencies.";
};
resourceType = lib.mkOption {
resourceType = mkOption {
type = lib.types.str;
default = "";
internal = true;
description = "The type of the podman Quadlet resource.";
};
serviceName = lib.mkOption {
serviceName = mkOption {
type = lib.types.str;
internal = true;
description = "The name of the systemd service.";
};
source = lib.mkOption {
source = mkOption {
type = lib.types.str;
internal = true;
description = "The quadlet source file content.";
@@ -42,13 +52,13 @@ in
{
options.services.podman = {
internal = {
quadletDefinitions = lib.mkOption {
quadletDefinitions = mkOption {
type = lib.types.listOf quadletInternalType;
default = { };
internal = true;
description = "List of quadlet source file content and service names.";
};
builtQuadlets = lib.mkOption {
builtQuadlets = mkOption {
type = with lib.types; attrsOf package;
default = { };
internal = true;
@@ -56,8 +66,11 @@ in
};
};
package = lib.mkPackageOption pkgs "podman" { };
enableTypeChecks = lib.mkEnableOption "type checks for podman quadlets";
enableTypeChecks = mkEnableOption "type checks for podman quadlets";
};
config = mkIf cfg.enable {
assertions = [
(assertions.assertPlatform "services.podman.enableTypeChecks" config pkgs lib.platforms.linux)
];
};
}

View File

@@ -5,20 +5,23 @@
...
}:
let
inherit (lib) mkIf mkOption mkMerge;
assertions = import ../assertions.nix { inherit lib; };
cfg = config.services.podman;
in
{
options.services.podman = {
autoUpdate = {
enable = lib.mkOption {
enable = mkOption {
type = lib.types.bool;
default = false;
default = pkgs.stdenv.hostPlatform.isLinux;
description = "Automatically update the podman images.";
};
onCalendar = lib.mkOption {
onCalendar = mkOption {
type = lib.types.str;
default = "Sun *-*-* 00:00";
default = lib.optionalString pkgs.stdenv.hostPlatform.isLinux "Sun *-*-* 00:00";
description = ''
The systemd `OnCalendar` expression for the update. See
{manpage}`systemd.time(7)` for a description of the format.
@@ -27,9 +30,14 @@ in
};
};
config = lib.mkIf cfg.enable (
lib.mkMerge [
(lib.mkIf cfg.autoUpdate.enable {
config = mkIf cfg.enable (mkMerge [
{
assertions = [
(assertions.assertPlatform "services.podman.networks" config pkgs lib.platforms.linux)
];
}
(mkIf pkgs.stdenv.hostPlatform.isLinux (mkMerge [
(mkIf cfg.autoUpdate.enable {
systemd.user.services."podman-auto-update" = {
Unit = {
Description = "Podman auto-update service";
@@ -86,6 +94,6 @@ in
}:/bin
'';
}
]
);
]))
]);
}

View File

@@ -5,7 +5,13 @@
...
}:
let
inherit (lib) mkOption types;
inherit (lib)
mkIf
mkOption
mkMerge
types
;
assertions = import ../assertions.nix { inherit lib; };
cfg = config.services.podman;
@@ -192,10 +198,17 @@ in
let
volumeQuadlets = lib.mapAttrsToList toQuadletInternal cfg.volumes;
in
lib.mkIf cfg.enable {
services.podman.internal.quadletDefinitions = volumeQuadlets;
assertions = lib.flatten (map (volume: volume.assertions) volumeQuadlets);
mkIf cfg.enable (mkMerge [
{
assertions = [
(assertions.assertPlatform "services.podman.volumes" config pkgs lib.platforms.linux)
];
}
(mkIf pkgs.stdenv.hostPlatform.isLinux {
services.podman.internal.quadletDefinitions = volumeQuadlets;
assertions = lib.flatten (map (volume: volume.assertions) volumeQuadlets);
xdg.configFile."podman/volumes.manifest".text = podman-lib.generateManifestText volumeQuadlets;
};
xdg.configFile."podman/volumes.manifest".text = podman-lib.generateManifestText volumeQuadlets;
})
]);
}

View File

@@ -1,30 +0,0 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager for podman build configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-bld.build
[X-Build]
Environment=
File=/nix/store/00000000000000000000000000000000-Containerfile
ImageTag=homemanager/my-bld
Label=nix.home-manager.managed=true
TLSVerify=true
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
RemainAfterExit=yes
TimeoutStartSec=300
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman build --tls-verify --tag homemanager/my-bld --label nix.home-manager.managed=true --file /nix/store/00000000000000000000000000000000-Containerfile
SyslogIdentifier=%N
Type=oneshot
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for build my-bld
SourcePath=/nix/store/00000000000000000000000000000000-home-build-podman-my-bld/quadlets/podman-my-bld.build
RequiresMountsFor=%t/containers

View File

@@ -1,50 +0,0 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager podman container configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-container.container
[X-Container]
AddDevice=/dev/null:/dev/null
AutoUpdate=registry
ContainerName=my-container
Entrypoint=/sleep.sh
Environment=VAL_A=A
Environment=VAL_B=2
Environment=VAL_C=false
Image=docker.io/alpine:latest
Label=nix.home-manager.managed=true
Network=mynet
NetworkAlias=test-alias-1
NetworkAlias=test-alias-2
PodmanArgs=--security-opt=no-new-privileges
PublishPort=8080:80
ReadOnlyTmpfs=true
Volume=/tmp:/tmp
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
Environment=PATH=/run/wrappers/bin:/run/current-system/sw/bin:@nftables@/bin:/home/hm-user/.nix-profile/bin:@systemd@/bin
Restart=on-failure
TimeoutStopSec=30
Environment=PODMAN_SYSTEMD_UNIT=%n
KillMode=mixed
ExecStop=/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i my-container
ExecStopPost=-/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i my-container
Delegate=yes
Type=notify
NotifyAccess=all
SyslogIdentifier=%N
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --name my-container --replace --rm --cgroups=split --entrypoint /sleep.sh --network-alias test-alias-1 --network-alias test-alias-2 --read-only-tmpfs --network mynet --sdnotify=conmon -d --device /dev/null:/dev/null -v /tmp:/tmp --label io.containers.autoupdate=registry --publish 8080:80 --env VAL_A=A --env VAL_B=2 --env VAL_C=false --label nix.home-manager.managed=true --security-opt=no-new-privileges docker.io/alpine:latest
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Before=fake.target
Description=home-manager test
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container/quadlets/podman-my-container.container
RequiresMountsFor=%t/containers
RequiresMountsFor=/tmp

View File

@@ -1,12 +0,0 @@
{ lib, pkgs, ... }:
lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
podman-configuration = ./configuration.nix;
podman-container = ./container.nix;
podman-build = ./build.nix;
podman-image = ./image.nix;
podman-integration = ./integration.nix;
podman-manifest = ./manifest.nix;
podman-network = ./network.nix;
podman-volume = ./volume.nix;
}

View File

@@ -1,30 +0,0 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager for podman build configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-bld.build
[X-Build]
Environment=
File=/nix/store/00000000000000000000000000000000-Containerfile
ImageTag=homemanager/my-bld
Label=nix.home-manager.managed=true
TLSVerify=true
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
RemainAfterExit=yes
TimeoutStartSec=300
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman build --tls-verify --tag homemanager/my-bld --label nix.home-manager.managed=true --file /nix/store/00000000000000000000000000000000-Containerfile
SyslogIdentifier=%N
Type=oneshot
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for build my-bld
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container-bld/quadlets/podman-my-bld.build
RequiresMountsFor=%t/containers

View File

@@ -1,38 +0,0 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager podman container configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-container-bld.container
[X-Container]
ContainerName=my-container-bld
Environment=
Image=podman-my-bld.build
Label=nix.home-manager.managed=true
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
Environment=PATH=/run/wrappers/bin:/run/current-system/sw/bin:@nftables@/bin:/home/hm-user/.nix-profile/bin:@systemd@/bin
Restart=always
TimeoutStopSec=30
Environment=PODMAN_SYSTEMD_UNIT=%n
KillMode=mixed
ExecStop=/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i my-container-bld
ExecStopPost=-/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i my-container-bld
Delegate=yes
Type=notify
NotifyAccess=all
SyslogIdentifier=%N
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --name my-container-bld --replace --rm --cgroups=split --sdnotify=conmon -d --label nix.home-manager.managed=true homemanager/my-bld
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for container my-container-bld
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container-bld/quadlets/podman-my-container-bld.container
RequiresMountsFor=%t/containers
Requires=podman-my-bld-build.service
After=podman-my-bld-build.service

View File

@@ -1,45 +0,0 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager podman container configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-container.container
[X-Container]
ContainerName=my-container
Environment=
Image=podman-my-img.image
Label=nix.home-manager.managed=true
Network=podman-my-app.network
Network=externalnet
Volume=podman-my-app.volume:/data
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
Environment=PATH=/run/wrappers/bin:/run/current-system/sw/bin:@nftables@/bin:/home/hm-user/.nix-profile/bin:@systemd@/bin
Restart=always
TimeoutStopSec=30
Environment=PODMAN_SYSTEMD_UNIT=%n
KillMode=mixed
ExecStop=/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i my-container
ExecStopPost=-/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i my-container
Delegate=yes
Type=notify
NotifyAccess=all
SyslogIdentifier=%N
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --name my-container --replace --rm --cgroups=split --network my-app --network externalnet --sdnotify=conmon -d -v my-app:/data --label nix.home-manager.managed=true docker.io/alpine:latest
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for container my-container
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container/quadlets/podman-my-container.container
RequiresMountsFor=%t/containers
Requires=podman-my-img-image.service
After=podman-my-img-image.service
Requires=podman-my-app-network.service
After=podman-my-app-network.service
Requires=podman-my-app-volume.service
After=podman-my-app-volume.service

View File

@@ -1,3 +1,4 @@
{ pkgs, ... }:
{
services.podman = {
enable = true;
@@ -46,6 +47,7 @@
storageFile=$configPath/storage.conf
mountsFile=$configPath/mounts.conf
# Check that config files are generated on both platforms
assertFileExists $containersFile
assertFileExists $policyFile
assertFileExists $registriesFile
@@ -63,5 +65,16 @@
assertFileContent $registriesFile ${./configuration-registries-expected.conf}
assertFileContent $storageFile ${./configuration-storage-expected.conf}
assertFileContent $mountsFile ${./configuration-mounts-expected.conf}
${
if pkgs.stdenv.hostPlatform.isDarwin then
''
# Darwin-specific: verify that config directory is automatically mounted into podman machines
assertFileExists activate
assertFileRegex activate '\$HOME/\.config/containers:/home/core/\.config/containers'
''
else
""
}
'';
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>KeepAlive</key>
<dict>
<key>Crashed</key>
<true/>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>Label</key>
<string>org.nix-community.home.podman-machine-podman-machine-default</string>
<key>ProcessType</key>
<string>Background</string>
<key>ProgramArguments</key>
<array>
<string>/nix/store/00000000000000000000000000000000-podman-machine-watchdog-podman-machine-default</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,35 @@
{
services.podman = {
enable = true;
useDefaultMachine = true;
};
nmt.script = ''
serviceDir=LaunchAgents
# Check that the default machine watchdog launchd service exists
agentFile=$serviceDir/org.nix-community.home.podman-machine-podman-machine-default.plist
assertFileExists $agentFile
# Normalize and verify agent file content
agentFileNormalized=$(normalizeStorePaths "$agentFile")
assertFileContent "$agentFileNormalized" ${./basic-expected-agent.plist}
# Verify home activation creates the default machine
assertFileExists activate
assertFileRegex activate 'podman-machine-default'
assertFileRegex activate 'podman machine init podman-machine-default'
assertFileNotRegex activate '[-][-]cpus'
assertFileNotRegex activate '[-][-]disk-size'
assertFileNotRegex activate '[-][-]image'
assertFileNotRegex activate '[-][-]memory'
assertFileNotRegex activate '[-][-]rootful'
assertFileNotRegex activate '[-][-]swap'
assertFileNotRegex activate '[-][-]timezone'
assertFileNotRegex activate '[-][-]username'
assertFileNotRegex activate '[-][-]volumes'
# Verify that config directory is automatically mounted into the machine
assertFileRegex activate '\$HOME/\.config/containers:/home/core/\.config/containers'
'';
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>KeepAlive</key>
<dict>
<key>Crashed</key>
<true/>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>Label</key>
<string>org.nix-community.home.podman-machine-dev-machine</string>
<key>ProcessType</key>
<string>Background</string>
<key>ProgramArguments</key>
<array>
<string>/nix/store/00000000000000000000000000000000-podman-machine-watchdog-dev-machine</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,63 @@
{
services.podman = {
enable = true;
useDefaultMachine = false;
machines = {
"dev-machine" = {
cpus = 8;
memory = 8192;
diskSize = 200;
rootful = true;
autoStart = true;
watchdogInterval = 60;
};
"test-machine" = {
cpus = 2;
memory = 4096;
diskSize = 50;
autoStart = false;
watchdogInterval = 30;
};
};
};
nmt.script = ''
serviceDir=LaunchAgents
# Check that dev-machine watchdog exists (has autoStart = true)
devAgentFile=$serviceDir/org.nix-community.home.podman-machine-dev-machine.plist
assertFileExists $devAgentFile
# Normalize and verify dev-machine agent file content
devAgentFileNormalized=$(normalizeStorePaths "$devAgentFile")
assertFileContent "$devAgentFileNormalized" ${./custom-machines-dev-expected-agent.plist}
# Check that test-machine watchdog does NOT exist (has autoStart = false)
testAgentFile=$serviceDir/org.nix-community.home.podman-machine-test-machine.plist
assertPathNotExists $testAgentFile
# Verify home activation creates both machines
assertFileExists activate
# Check dev-machine initialization with custom parameters
assertFileRegex activate 'dev-machine'
assertFileRegex activate 'podman machine init dev-machine'
assertFileRegex activate '[-][-]cpus 8'
assertFileRegex activate '[-][-]memory 8192'
assertFileRegex activate '[-][-]disk-size 200'
assertFileRegex activate '[-][-]rootful'
# Check test-machine initialization
assertFileRegex activate 'test-machine'
assertFileRegex activate 'podman machine init test-machine'
assertFileRegex activate '[-][-]cpus 2'
assertFileRegex activate '[-][-]memory 4096'
assertFileRegex activate '[-][-]disk-size 50'
# Verify default machine is NOT created
assertFileNotRegex activate 'podman-machine-default'
# Verify that config directory is automatically mounted into all machines
assertFileRegex activate '\$HOME/\.config/containers:/home/core/\.config/containers'
'';
}

View File

@@ -0,0 +1,5 @@
{
podman-darwin-basic = ./basic.nix;
podman-darwin-custom-machines = ./custom-machines.nix;
podman-darwin-no-default-machine = ./no-default-machine.nix;
}

View File

@@ -0,0 +1,20 @@
{
services.podman = {
enable = true;
useDefaultMachine = false;
machines = { };
};
nmt.script = ''
serviceDir=LaunchAgents
# Check that the default machine watchdog launchd service does not exists
agentFile=$serviceDir/org.nix-community.home.podman-machine-podman-machine-default.plist
assertPathNotExists $agentFile
# Verify home activation script doesn't create default machine
activationScript=activate
assertFileExists $activationScript
assertFileNotRegex $activationScript 'podman-machine-default'
'';
}

View File

@@ -0,0 +1,6 @@
{ lib, pkgs, ... }:
{
podman-configuration = ./configuration.nix;
}
// (lib.optionalAttrs pkgs.stdenv.hostPlatform.isDarwin (import ./darwin/default.nix))
// (lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux (import ./linux/default.nix))

View File

@@ -0,0 +1,30 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager for podman build configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-bld.build
[X-Build]
Environment=
File=/nix/store/00000000000000000000000000000000-Containerfile
ImageTag=homemanager/my-bld
Label=nix.home-manager.managed=true
TLSVerify=true
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
RemainAfterExit=yes
TimeoutStartSec=300
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman build --tls-verify --tag homemanager/my-bld --label nix.home-manager.managed=true --file /nix/store/00000000000000000000000000000000-Containerfile
SyslogIdentifier=%N
Type=oneshot
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for build my-bld
SourcePath=/nix/store/00000000000000000000000000000000-home-build-podman-my-bld/quadlets/podman-my-bld.build
RequiresMountsFor=%t/containers

View File

@@ -0,0 +1,50 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager podman container configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-container.container
[X-Container]
AddDevice=/dev/null:/dev/null
AutoUpdate=registry
ContainerName=my-container
Entrypoint=/sleep.sh
Environment=VAL_A=A
Environment=VAL_B=2
Environment=VAL_C=false
Image=docker.io/alpine:latest
Label=nix.home-manager.managed=true
Network=mynet
NetworkAlias=test-alias-1
NetworkAlias=test-alias-2
PodmanArgs=--security-opt=no-new-privileges
PublishPort=8080:80
ReadOnlyTmpfs=true
Volume=/tmp:/tmp
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
Environment=PATH=/run/wrappers/bin:/run/current-system/sw/bin:@nftables@/bin:/home/hm-user/.nix-profile/bin:@systemd@/bin
Restart=on-failure
TimeoutStopSec=30
Environment=PODMAN_SYSTEMD_UNIT=%n
KillMode=mixed
ExecStop=/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i my-container
ExecStopPost=-/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i my-container
Delegate=yes
Type=notify
NotifyAccess=all
SyslogIdentifier=%N
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --name my-container --replace --rm --cgroups=split --entrypoint /sleep.sh --network-alias test-alias-1 --network-alias test-alias-2 --read-only-tmpfs --network mynet --sdnotify=conmon -d --device /dev/null:/dev/null -v /tmp:/tmp --label io.containers.autoupdate=registry --publish 8080:80 --env VAL_A=A --env VAL_B=2 --env VAL_C=false --label nix.home-manager.managed=true --security-opt=no-new-privileges docker.io/alpine:latest
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Before=fake.target
Description=home-manager test
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container/quadlets/podman-my-container.container
RequiresMountsFor=%t/containers
RequiresMountsFor=/tmp

View File

@@ -0,0 +1,9 @@
{
podman-linux-container = ./container.nix;
podman-linux-build = ./build.nix;
podman-linux-image = ./image.nix;
podman-linux-integration = ./integration.nix;
podman-linux-manifest = ./manifest.nix;
podman-linux-network = ./network.nix;
podman-linux-volume = ./volume.nix;
}

View File

@@ -0,0 +1,30 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager for podman build configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-bld.build
[X-Build]
Environment=
File=/nix/store/00000000000000000000000000000000-Containerfile
ImageTag=homemanager/my-bld
Label=nix.home-manager.managed=true
TLSVerify=true
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
RemainAfterExit=yes
TimeoutStartSec=300
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman build --tls-verify --tag homemanager/my-bld --label nix.home-manager.managed=true --file /nix/store/00000000000000000000000000000000-Containerfile
SyslogIdentifier=%N
Type=oneshot
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for build my-bld
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container-bld/quadlets/podman-my-bld.build
+RequiresMountsFor=%t/containers

View File

@@ -0,0 +1,40 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager podman container configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-container-bld.container
[X-Container]
ContainerName=my-container-bld
Environment=
Image=podman-my-bld.build
Label=nix.home-manager.managed=true
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
+Environment=PATH=/run/wrappers/bin:/run/current-system/sw/bin:@nftables@/bin:/home/hm-user/.nix-profile/bin:@systemd@/bin
Restart=always
TimeoutStopSec=30
Environment=PODMAN_SYSTEMD_UNIT=%n
KillMode=mixed
ExecStop=/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i my-container-bld
ExecStopPost=-/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i my-container-bld
Delegate=yes
Type=notify
NotifyAccess=all
SyslogIdentifier=%N
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --name my-container-bld --replace --rm --cgroups=split --sdnotify=conmon -d --label nix.home-manager.managed=true homemanager/my-bld
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for container my-container-bld
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container-bld/quadlets/podman-my-container-bld.container
RequiresMountsFor=%t/containers
Requires=podman-my-bld-build.service
After=podman-my-bld-build.service
+++++++ Contents of side #2
>>>>>>> Conflict 1 of 1 ends

View File

@@ -0,0 +1,45 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager podman container configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-container.container
[X-Container]
ContainerName=my-container
Environment=
Image=podman-my-img.image
Label=nix.home-manager.managed=true
Network=podman-my-app.network
Network=externalnet
Volume=podman-my-app.volume:/data
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
Environment=PATH=/run/wrappers/bin:/run/current-system/sw/bin:@nftables@/bin:/home/hm-user/.nix-profile/bin:@systemd@/bin
Restart=always
TimeoutStopSec=30
Environment=PODMAN_SYSTEMD_UNIT=%n
KillMode=mixed
ExecStop=/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i my-container
ExecStopPost=-/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i my-container
Delegate=yes
Type=notify
NotifyAccess=all
SyslogIdentifier=%N
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --name my-container --replace --rm --cgroups=split --network my-app --network externalnet --sdnotify=conmon -d -v my-app:/data --label nix.home-manager.managed=true docker.io/alpine:latest
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for container my-container
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container/quadlets/podman-my-container.container
RequiresMountsFor=%t/containers
Requires=podman-my-img-image.service
After=podman-my-img-image.service
Requires=podman-my-app-network.service
After=podman-my-app-network.service
Requires=podman-my-app-volume.service
After=podman-my-app-volume.service