{ 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 is terminated at Caddy (see the host's # configuration.nix for the public vhost); Zitadel itself only listens on # localhost:8080. # # The "zitadel" Postgres role doubles as both Database.postgres.User (normal # runtime queries) and .Admin (bootstrap: creates the db/extensions on first # start) — granted createdb+createrole instead of using the real postgres # superuser, matching Zitadel's own guidance to keep bootstrap privileges # scoped 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"; }; }; }; }; }