From 72672bd2e02a77cac4f62f3e22d0e50b72383ee4 Mon Sep 17 00:00:00 2001 From: Gerhard Schwanzer Date: Wed, 8 Jul 2026 10:11:39 +0200 Subject: [PATCH] nixos/part-db: add environmentFile option Generate env.local at runtime so secrets such as APP_SECRET can be provided outside the Nix store. Assisted-by: pi coding agent / Mika (OpenAI GPT-5.5) --- nixos/modules/services/web-apps/part-db.nix | 47 ++++++++++++++++++--- nixos/tests/web-apps/part-db.nix | 9 +++- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/nixos/modules/services/web-apps/part-db.nix b/nixos/modules/services/web-apps/part-db.nix index 98c19d6f2db9..006481e6dda7 100644 --- a/nixos/modules/services/web-apps/part-db.nix +++ b/nixos/modules/services/web-apps/part-db.nix @@ -8,6 +8,11 @@ let cfg = config.services.part-db; pkg = cfg.package; + envFile = pkgs.writeText "part-db-env" ( + lib.concatStringsSep "\n" (lib.mapAttrsToList (key: value: "${key}=\"${value}\"") cfg.settings) + + "\n" + ); + inherit (lib) mkEnableOption mkPackageOption @@ -62,6 +67,17 @@ in ''; }; + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/secrets/part-db.env"; + description = '' + Path to a file containing extra Part-DB environment variables in dotenv + format. This can be used for secrets such as `APP_SECRET` without + putting them in the Nix store. + ''; + }; + poolConfig = lib.mkOption { type = lib.types.attrsOf ( lib.types.oneOf [ @@ -213,10 +229,32 @@ in systemd = { services = { + part-db-setup = { + before = [ "part-db-migrate.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + restartTriggers = [ envFile ]; + script = '' + install -Dm0600 -o part-db -g part-db ${envFile} /var/lib/part-db/env.local + '' + + lib.optionalString (cfg.environmentFile != null) '' + cat ${lib.escapeShellArg cfg.environmentFile} >> /var/lib/part-db/env.local + ''; + }; + part-db-migrate = { before = [ "phpfpm-part-db.service" ]; - after = [ "postgresql.target" ]; - requires = [ "postgresql.target" ]; + after = [ + "postgresql.target" + "part-db-setup.service" + ]; + requires = [ + "postgresql.target" + "part-db-setup.service" + ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "oneshot"; @@ -251,11 +289,6 @@ in user = "part-db"; group = "part-db"; }; - "/var/lib/part-db/env.local"."L+" = { - argument = "${pkgs.writeText "part-db-env" ( - lib.concatStringsSep "\n" (lib.mapAttrsToList (key: value: "${key}=\"${value}\"") cfg.settings) - )}"; - }; "/var/lib/part-db/".d = { mode = "0755"; user = "part-db"; diff --git a/nixos/tests/web-apps/part-db.nix b/nixos/tests/web-apps/part-db.nix index 4f65ccb4d6f3..5e96774c2730 100644 --- a/nixos/tests/web-apps/part-db.nix +++ b/nixos/tests/web-apps/part-db.nix @@ -5,7 +5,12 @@ nodes = { machine = { - services.part-db.enable = true; + services.part-db = { + enable = true; + environmentFile = pkgs.writeText "part-db.env" '' + APP_SECRET=0123456789abcdef0123456789abcdef + ''; + }; }; }; @@ -21,6 +26,8 @@ machine.succeed("test -d /var/lib/part-db/share") machine.succeed("test $(readlink ${pkgs.part-db}/public/media) = /var/lib/part-db/public/media/") machine.succeed("test $(readlink ${pkgs.part-db}/uploads) = /var/lib/part-db/uploads/") + machine.succeed("grep APP_SECRET=0123456789abcdef0123456789abcdef /var/lib/part-db/env.local") + machine.succeed("test $(stat -c %a:%U:%G /var/lib/part-db/env.local) = 600:part-db:part-db") machine.wait_for_open_port(80)