nixos/oci-containers: stricter dependencies for rootless containers with sdnotify=healthy

After running this configuration for a while, we
noticed that the containers didn't get back up once and the services
failed with the following error:

    Error: current system boot ID differs from cached boot ID; an unhandled reboot has occurred.

This is hard to reproduce and seems to be a timing issue. However,
the logs indicated another issue that this patch now solves:

* The ExecStartPost= indicated that the user session got stopped before
  which is required or sdnotify=healthy. Add explicit ordering for
  user@. This unfortunately requires a statically declared uid.
This commit is contained in:
Maximilian Bosch 2025-03-28 17:14:42 +01:00
parent 18cff5e982
commit 344ee0cf07
No known key found for this signature in database
2 changed files with 25 additions and 3 deletions

View file

@ -434,6 +434,7 @@ let
}; };
effectiveUser = container.podman.user or "root"; effectiveUser = container.podman.user or "root";
inherit (config.users.users.${effectiveUser}) uid;
dependOnLingerService = dependOnLingerService =
cfg.backend == "podman" && effectiveUser != "root" && config.users.users.${effectiveUser}.linger; cfg.backend == "podman" && effectiveUser != "root" && config.users.users.${effectiveUser}.linger;
in in
@ -441,7 +442,7 @@ let
wantedBy = [ ] ++ optional (container.autoStart) "multi-user.target"; wantedBy = [ ] ++ optional (container.autoStart) "multi-user.target";
wants = wants =
lib.optional (container.imageFile == null && container.imageStream == null) "network-online.target" lib.optional (container.imageFile == null && container.imageStream == null) "network-online.target"
++ lib.optional dependOnLingerService "linger-users.service"; ++ lib.optionals dependOnLingerService [ "linger-users.service" ];
after = after =
lib.optionals (cfg.backend == "docker") [ lib.optionals (cfg.backend == "docker") [
"docker.service" "docker.service"
@ -452,8 +453,15 @@ let
"network-online.target" "network-online.target"
] ]
++ dependsOn ++ dependsOn
++ lib.optional dependOnLingerService "linger-users.service"; ++ lib.optionals dependOnLingerService [ "linger-users.service" ]
requires = dependsOn; ++ lib.optionals (effectiveUser != "root" && container.podman.sdnotify == "healthy") [
"user@${toString uid}.service"
];
requires =
dependsOn
++ lib.optionals (effectiveUser != "root" && container.podman.sdnotify == "healthy") [
"user@${toString uid}.service"
];
environment = lib.mkMerge [ environment = lib.mkMerge [
proxy_env proxy_env
(mkIf (cfg.backend == "podman" && container.podman.user != "root") { (mkIf (cfg.backend == "podman" && container.podman.user != "root") {
@ -523,6 +531,10 @@ let
else else
"${cfg.backend} rm -f ${name} || true"; "${cfg.backend} rm -f ${name} || true";
unitConfig = mkIf (effectiveUser != "root") {
RequiresMountsFor = "/run/user/${toString uid}/containers";
};
serviceConfig = serviceConfig =
{ {
### There is no generalized way of supporting `reload` for docker ### There is no generalized way of supporting `reload` for docker
@ -616,6 +628,15 @@ in
assertion = cfg.backend == "docker" -> podman == null; assertion = cfg.backend == "docker" -> podman == null;
message = "virtualisation.oci-containers.containers.${name}: Cannot set `podman` option if backend is `docker`."; message = "virtualisation.oci-containers.containers.${name}: Cannot set `podman` option if backend is `docker`.";
} }
{
assertion =
cfg.backend == "podman" && podman.sdnotify == "healthy" && podman.user != "root"
-> config.users.users.${podman.user}.uid != null;
message = ''
Rootless container ${name} (with podman and sdnotify=healthy)
requires that its running user ${podman.user} has a statically specified uid.
'';
}
]; ];
in in
concatMap (name: toAssertions name cfg.containers.${name}) (lib.attrNames cfg.containers); concatMap (name: toAssertions name cfg.containers.${name}) (lib.attrNames cfg.containers);

View file

@ -80,6 +80,7 @@ let
home = "/var/lib/redis"; home = "/var/lib/redis";
linger = type == "healthy"; linger = type == "healthy";
createHome = true; createHome = true;
uid = 2342;
subUidRanges = [ subUidRanges = [
{ {
count = 65536; count = 65536;