Skip to content

Commit 437cbdf

Browse files
committed
fix: resolve user-scope/system-scope dep mismatch for native apps
Two boot failures fixed: 1. podman-apps-network.service was missing PATH=/run/wrappers, causing "newuidmap not found" on every boot. Authentik containers depended on this network service, so they never started. 2. metadata.nix auto-wires native app deps (postgres, redis) as Requires= in user-scope podman services. But postgres.service and redis.service are system-scope, invisible to user units, causing "Unit postgres.service not found" on start. Fix: native apps now expose user-scope readiness units activated via inotify (no polling): - postgres: systemd.user.paths.postgres watches the actual socket at /run/postgresql/.s.PGSQL.5432 - All mkNativeApp apps: system service writes /run/bloud-ready/{name} in ExecStartPost (+prefix for root), user-scope path unit watches that file and activates a no-op {name}.service alias - /run/bloud-ready/ created via systemd.tmpfiles.rules Any future native app added via mkNativeApp gets this for free.
1 parent ec2a44a commit 437cbdf

File tree

6 files changed

+55
-7
lines changed

6 files changed

+55
-7
lines changed

apps/postgres/module.nix

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,24 @@ in
7272
}];
7373
};
7474

75+
# User-scope readiness for postgres: watch the actual socket via inotify.
76+
# The socket appearing IS the readiness signal — no sentinel file needed.
77+
# Podman container services declare Requires=postgres.service (user scope).
78+
systemd.user.paths.postgres = {
79+
description = "Watch for PostgreSQL socket";
80+
pathConfig.PathExists = "/run/postgresql/.s.PGSQL.5432";
81+
wantedBy = [ "default.target" ];
82+
};
83+
84+
systemd.user.services.postgres = {
85+
description = "PostgreSQL ready (user scope alias)";
86+
serviceConfig = {
87+
Type = "oneshot";
88+
RemainAfterExit = true;
89+
ExecStart = "${pkgs.coreutils}/bin/true";
90+
};
91+
};
92+
7593
# Canonical alias so app modules can declare `postgres.service` as a dependency
7694
# without knowing the NixOS-generated service name (postgresql.service).
7795
# Convention: native apps expose {appName}.service for systemd dependency tracking.

claude.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ Set `BLOUD_PVE_HOST` in your `.env` file or environment, then:
376376

377377
`BLOUD_PVE_HOST` can be set in a `.env` file at the project root — the CLI loads it automatically:
378378
```
379-
BLOUD_PVE_HOST=root@192.168.0.62
379+
BLOUD_PVE_HOST=root@10.0.0.165
380380
```
381381

382382
### After Changing NixOS Config

cli/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ func printUsage() {
219219
fmt.Println(" setup-builder Provision BLOUD_BUILDER_HOST with Go + Node via Nix")
220220
fmt.Println()
221221
fmt.Println("Environment:")
222-
fmt.Println(" BLOUD_PVE_HOST Proxmox SSH target (e.g. root@192.168.0.62)")
222+
fmt.Println(" BLOUD_PVE_HOST Proxmox SSH target (e.g. root@10.0.0.165)")
223223
fmt.Println(" BLOUD_PVE_VMID VM ID (default: 9999)")
224224
fmt.Println(" BLOUD_BUILDER_HOST SSH target for local ISO builds (e.g. builder@192.168.0.105)")
225225
fmt.Println()

nixos/bloud.nix

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ in
9191
chown -R ${cfg.user}:users /home/${cfg.user}/.local/share/${cfg.dataDir}/media
9292
'';
9393

94+
# Create /run/bloud-ready/ for native app readiness sentinels.
95+
# Native apps write {name} here on ExecStartPost; user-scope path units watch
96+
# for these files via inotify to signal readiness to podman container services.
97+
systemd.tmpfiles.rules = [ "d /run/bloud-ready 0755 root root -" ];
98+
9499
# Enable rootless Podman
95100
virtualisation.podman = {
96101
enable = true;
@@ -114,6 +119,7 @@ in
114119
description = "Create podman network for apps stack";
115120
wantedBy = [ "bloud-apps.target" ];
116121
before = [ "bloud-apps.target" ];
122+
path = [ "/run/wrappers" pkgs.podman ];
117123
serviceConfig = {
118124
Type = "oneshot";
119125
RemainAfterExit = true;

nixos/lib/metadata.nix

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Read a metadata.yaml file and return native systemd service deps.
22
#
33
# Reads integrations.*.compatible[].app entries and maps each to "{app}.service".
4-
# Native apps (postgres, redis) expose canonical {appName}.service aliases; other
5-
# app names resolve to non-existent services and are silently ignored by systemd.
4+
# Native apps expose user-scope {name}.service aliases (via path units) so these
5+
# can be used in both After= and Requires= of user-scope podman container services.
66
#
77
# Usage:
88
# nativeDeps = import ../../nixos/lib/metadata.nix { inherit pkgs lib; };

nixos/lib/native-app.nix

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,35 @@ let
8989
# Uses + prefix so hooks run as root — needed because native services run
9090
# as their own system users (not as the bloud user), but the configurator
9191
# binary and its output files are owned by the bloud user.
92-
# NOTE: If a future app's NixOS module already sets ExecStartPre/Post,
93-
# use a list here and handle ordering per-app.
92+
# ExecStartPost uses mkBefore so the configurator runs before the readiness sentinel.
9493
hookConfig = lib.optionalAttrs (configuratorHooks && serviceName != null) {
9594
systemd.services.${serviceName}.serviceConfig = {
9695
ExecStartPre = lib.mkBefore [ "+${bloudCfg.agentPath} configure prestart ${name}" ];
97-
ExecStartPost = [ "+${bloudCfg.agentPath} configure poststart ${name}" ];
96+
ExecStartPost = lib.mkBefore [ "+${bloudCfg.agentPath} configure poststart ${name}" ];
97+
};
98+
};
99+
100+
# Readiness convention for native apps:
101+
# - System service writes /run/bloud-ready/{name} in ExecStartPost (after configurator)
102+
# - User-scope path unit watches for this file via inotify (no polling)
103+
# - User-scope service is activated by the path unit, providing a dep target
104+
# that podman container services can declare Requires= on
105+
readinessConfig = lib.optionalAttrs (serviceName != null) {
106+
systemd.services.${serviceName}.serviceConfig = {
107+
ExecStartPost = lib.mkAfter [ "+${pkgs.coreutils}/bin/touch /run/bloud-ready/${name}" ];
108+
};
109+
systemd.user.paths.${name} = {
110+
description = "Watch for ${name} readiness sentinel";
111+
pathConfig.PathExists = "/run/bloud-ready/${name}";
112+
wantedBy = [ "default.target" ];
113+
};
114+
systemd.user.services.${name} = {
115+
description = "${name} ready (user scope alias)";
116+
serviceConfig = {
117+
Type = "oneshot";
118+
RemainAfterExit = true;
119+
ExecStart = "${pkgs.coreutils}/bin/true";
120+
};
98121
};
99122
};
100123

@@ -112,6 +135,7 @@ in
112135
config = lib.mkIf appCfg.enable (lib.mkMerge [
113136
resolvedNixosConfig
114137
hookConfig
138+
readinessConfig
115139
mediaConfig
116140
]);
117141
}

0 commit comments

Comments
 (0)