{ config, lib, pkgs, ... }: let enabledDatabases = lib.filterAttrs (_: db: db.enable) config.services.postgresql.databases; extraDatabasesList = config.services.postgresql.extraDatabases; serviceDatabaseUsers = lib.mapAttrsToList (_: db: { name = db.user; ensureDBOwnership = true; }) (lib.filterAttrs (_: db: db.ensureUser) enabledDatabases); extraDatabaseUsers = builtins.map (dbName: { name = dbName; ensureDBOwnership = true; }) extraDatabasesList; serviceDatabases = lib.mapAttrsToList (_: db: db.database) enabledDatabases; extraDatabaseNames = extraDatabasesList; serviceUserMappings = lib.mapAttrsToList (_: db: "user_map ${db.user} ${db.user}") enabledDatabases; extraUserMappings = builtins.map (dbName: "user_map ${dbName} ${dbName}") extraDatabasesList; builtinServiceMappings = let forgejoMapping = lib.optional (config.services.forgejo.enable && config.services.forgejo.database.type == "postgres") "user_map forgejo forgejo"; immichMapping = lib.optional (config.services.immich.enable && config.services.immich.database.enable) "user_map immich immich"; paperlessMapping = lib.optional (config.services.paperless.enable && config.services.paperless.database.createLocally) "user_map paperless paperless"; in forgejoMapping ++ immichMapping ++ paperlessMapping; in { options = { services.postgresql = { databases = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { options = { enable = lib.mkOption { type = lib.types.bool; default = false; description = "Whether to create this database and user"; }; user = lib.mkOption { type = lib.types.str; default = name; description = "Database user name"; }; database = lib.mkOption { type = lib.types.str; default = name; description = "Database name"; }; ensureUser = lib.mkOption { type = lib.types.bool; default = true; description = "Whether to ensure the user exists"; }; }; })); default = {}; description = "Databases to create for services"; }; extraDatabases = lib.mkOption { type = lib.types.listOf lib.types.str; default = []; description = "Additional databases to create (user name will match database name)"; example = ["custom_db" "test_db"]; }; adminUsers = lib.mkOption { type = lib.types.listOf lib.types.str; default = []; description = "System users who should have PostgreSQL superuser access"; example = ["leyla" "admin"]; }; }; }; config = lib.mkIf config.services.postgresql.enable { services = { postgresql = { package = pkgs.postgresql_16; ensureUsers = [ {name = "postgres";} ] ++ serviceDatabaseUsers ++ extraDatabaseUsers; ensureDatabases = serviceDatabases ++ extraDatabaseNames; identMap = '' # ArbitraryMapName systemUser DBUser # Administration Users superuser_map root postgres superuser_map postgres postgres '' + ( lib.strings.concatLines (builtins.map (user: "superuser_map ${user} postgres") config.services.postgresql.adminUsers) ) + '' # Client Users '' + ( lib.strings.concatLines (serviceUserMappings ++ extraUserMappings ++ builtinServiceMappings) ); authentication = pkgs.lib.mkOverride 10 '' # type database DBuser origin-address auth-method optional_ident_map local all postgres peer map=superuser_map local sameuser all peer map=user_map ''; }; }; }; }