{ config, ... }: # Zitadel — self-hosted identity/OIDC provider. Local Postgres (peer-authed # over the unix socket, no password anywhere) since Zitadel is latency- # sensitive to its DB. TLS terminates at Caddy; Zitadel itself only listens # on localhost:8080. # # The "zitadel" Postgres role doubles as both User (runtime queries) and # Admin (bootstrap: creates db/extensions on first start) — granted # createdb+createrole rather than using the postgres superuser, per # Zitadel's own guidance to scope bootstrap privileges to a dedicated role. # # Needs, wired via sops in the host's secrets.nix: # - masterKeyFile: 32 raw bytes, e.g. `openssl rand -hex 16` # - extraStepsPaths: a FirstInstance admin bootstrap file (keeps the admin # password out of the Nix store — settings.steps would leak it, since # it's rendered into a world-readable store path) { services.postgresql = { enable = true; ensureDatabases = [ "zitadel" ]; ensureUsers = [ { name = "zitadel"; ensureDBOwnership = true; ensureClauses = { createdb = true; createrole = true; }; } ]; }; services.zitadel = { enable = true; tlsMode = "external"; masterKeyFile = config.sops.secrets.zitadel_master_key.path; extraStepsPaths = [ config.sops.templates."zitadel-first-instance.yaml".path ]; settings = { Port = 8080; ExternalPort = 443; ExternalSecure = true; Database.postgres = { Host = "/run/postgresql"; Port = 5432; Database = "zitadel"; User = { Username = "zitadel"; SSL.Mode = "disable"; }; Admin = { Username = "zitadel"; SSL.Mode = "disable"; }; }; }; }; }