summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--nixos/modules/services/databases/postgresql.nix65
-rw-r--r--nixos/modules/services/development/zammad.nix4
-rw-r--r--nixos/modules/services/finance/odoo.nix2
-rw-r--r--nixos/modules/services/mail/listmonk.nix2
-rw-r--r--nixos/modules/services/mail/roundcube.nix14
-rw-r--r--nixos/modules/services/mail/sympa.nix10
-rw-r--r--nixos/modules/services/matrix/matrix-sliding-sync.nix4
-rw-r--r--nixos/modules/services/matrix/mautrix-facebook.nix4
-rw-r--r--nixos/modules/services/misc/atuin.nix4
-rw-r--r--nixos/modules/services/misc/forgejo.nix10
-rw-r--r--nixos/modules/services/misc/gitea.nix10
-rw-r--r--nixos/modules/services/misc/redmine.nix4
-rw-r--r--nixos/modules/services/misc/sourcehut/service.nix11
-rw-r--r--nixos/modules/services/monitoring/zabbix-proxy.nix4
-rw-r--r--nixos/modules/services/monitoring/zabbix-server.nix4
-rw-r--r--nixos/modules/services/security/hockeypuck.nix2
-rw-r--r--nixos/modules/services/web-apps/coder.nix10
-rw-r--r--nixos/modules/services/web-apps/gotosocial.nix4
-rw-r--r--nixos/modules/services/web-apps/invidious.nix6
-rw-r--r--nixos/modules/services/web-apps/lemmy.nix2
-rw-r--r--nixos/modules/services/web-apps/mastodon.nix6
-rw-r--r--nixos/modules/services/web-apps/mediawiki.nix4
-rw-r--r--nixos/modules/services/web-apps/miniflux.nix17
-rw-r--r--nixos/modules/services/web-apps/mobilizon.nix10
-rw-r--r--nixos/modules/services/web-apps/moodle.nix4
-rw-r--r--nixos/modules/services/web-apps/netbox.nix4
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix2
-rw-r--r--nixos/modules/services/web-apps/onlyoffice.nix2
-rw-r--r--nixos/modules/services/web-apps/outline.nix2
-rw-r--r--nixos/modules/services/web-apps/peering-manager.nix4
-rw-r--r--nixos/modules/services/web-apps/pixelfed.nix1
-rw-r--r--nixos/modules/services/web-apps/tt-rss.nix13
-rw-r--r--nixos/modules/services/web-servers/hydron.nix2
-rw-r--r--nixos/tests/dex-oidc.nix2
-rw-r--r--nixos/tests/ferretdb.nix2
-rw-r--r--nixos/tests/freshrss-pgsql.nix4
-rw-r--r--nixos/tests/grafana/basic.nix2
-rw-r--r--nixos/tests/hockeypuck.nix2
-rw-r--r--nixos/tests/home-assistant.nix12
-rw-r--r--nixos/tests/paperless.nix2
-rw-r--r--nixos/tests/pgadmin4.nix8
-rw-r--r--nixos/tests/pgbouncer.nix12
-rw-r--r--nixos/tests/powerdns-admin.nix4
-rw-r--r--nixos/tests/sftpgo.nix2
-rw-r--r--nixos/tests/tandoor-recipes.nix23
-rw-r--r--nixos/tests/vikunja.nix2
-rw-r--r--nixos/tests/wiki-js.nix5
47 files changed, 176 insertions, 153 deletions
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index af4db5c9611..3d2205b6355 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -165,25 +165,13 @@ in
               '';
             };
 
-            ensurePermissions = mkOption {
-              type = types.attrsOf types.str;
-              default = {};
-              description = lib.mdDoc ''
-                Permissions to ensure for the user, specified as an attribute set.
-                The attribute names specify the database and tables to grant the permissions for.
-                The attribute values specify the permissions to grant. You may specify one or
-                multiple comma-separated SQL privileges here.
-
-                For more information on how to specify the target
-                and on which privileges exist, see the
-                [GRANT syntax](https://www.postgresql.org/docs/current/sql-grant.html).
-                The attributes are used as `GRANT ''${attrValue} ON ''${attrName}`.
-              '';
-              example = literalExpression ''
-                {
-                  "DATABASE \"nextcloud\"" = "ALL PRIVILEGES";
-                  "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
-                }
+            ensureDBOwnership = mkOption {
+              type = types.bool;
+              default = false;
+              description = mdDoc ''
+                Grants the user ownership to a database with the same name.
+                This database must be defined manually in
+                [](#opt-services.postgresql.ensureDatabases).
               '';
             };
 
@@ -338,26 +326,21 @@ in
         });
         default = [];
         description = lib.mdDoc ''
-          Ensures that the specified users exist and have at least the ensured permissions.
+          Ensures that the specified users exist.
           The PostgreSQL users will be identified using peer authentication. This authenticates the Unix user with the
           same name only, and that without the need for a password.
-          This option will never delete existing users or remove permissions, especially not when the value of this
-          option is changed. This means that users created and permissions assigned once through this option or
-          otherwise have to be removed manually.
+          This option will never delete existing users or remove DB ownership of databases
+          once granted with `ensureDBOwnership = true;`. This means that this must be
+          cleaned up manually when changing after changing the config in here.
         '';
         example = literalExpression ''
           [
             {
               name = "nextcloud";
-              ensurePermissions = {
-                "DATABASE nextcloud" = "ALL PRIVILEGES";
-              };
             }
             {
               name = "superuser";
-              ensurePermissions = {
-                "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
-              };
+              ensureDBOwnership = true;
             }
           ]
         '';
@@ -445,6 +428,19 @@ in
 
   config = mkIf cfg.enable {
 
+    assertions = [
+      {
+        assertion = all
+          ({ name, ensureDBOwnership, ... }: ensureDBOwnership -> builtins.elem name cfg.ensureDatabases)
+          cfg.ensureUsers;
+        message = ''
+          For each database user defined with `services.postgresql.ensureUsers` and
+          `ensureDBOwnership = true;`, a database with the same name must be defined
+          in `services.postgresql.ensureDatabases`.
+        '';
+      }
+    ];
+
     services.postgresql.settings =
       {
         hba_file = "${pkgs.writeText "pg_hba.conf" cfg.authentication}";
@@ -557,11 +553,9 @@ in
               concatMapStrings
               (user:
                 let
-                  userPermissions = concatStringsSep "\n"
-                    (mapAttrsToList
-                      (database: permission: ''$PSQL -tAc 'GRANT ${permission} ON ${database} TO "${user.name}"' '')
-                      user.ensurePermissions
-                    );
+                  dbOwnershipStmt = optionalString
+                    user.ensureDBOwnership
+                    ''$PSQL -tAc 'ALTER DATABASE "${user.name}" OWNER TO "${user.name}";' '';
 
                   filteredClauses = filterAttrs (name: value: value != null) user.ensureClauses;
 
@@ -570,8 +564,9 @@ in
                   userClauses = ''$PSQL -tAc 'ALTER ROLE "${user.name}" ${concatStringsSep " " clauseSqlStatements}' '';
                 in ''
                   $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"'
-                  ${userPermissions}
                   ${userClauses}
+
+                  ${dbOwnershipStmt}
                 ''
               )
               cfg.ensureUsers
diff --git a/nixos/modules/services/development/zammad.nix b/nixos/modules/services/development/zammad.nix
index 7dd143eebf1..d24ed24ef39 100644
--- a/nixos/modules/services/development/zammad.nix
+++ b/nixos/modules/services/development/zammad.nix
@@ -204,7 +204,7 @@ in
 
     assertions = [
       {
-        assertion = cfg.database.createLocally -> cfg.database.user == "zammad";
+        assertion = cfg.database.createLocally -> cfg.database.user == "zammad" && cfg.database.name == "zammad";
         message = "services.zammad.database.user must be set to \"zammad\" if services.zammad.database.createLocally is set to true";
       }
       {
@@ -231,7 +231,7 @@ in
       ensureUsers = [
         {
           name = cfg.database.user;
-          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/finance/odoo.nix b/nixos/modules/services/finance/odoo.nix
index eec7c4e30cc..b8574ed09af 100644
--- a/nixos/modules/services/finance/odoo.nix
+++ b/nixos/modules/services/finance/odoo.nix
@@ -121,7 +121,7 @@ in
       ensureDatabases = [ "odoo" ];
       ensureUsers = [{
         name = "odoo";
-        ensurePermissions = { "DATABASE odoo" = "ALL PRIVILEGES"; };
+        ensureDBOwnership = true;
       }];
     };
   });
diff --git a/nixos/modules/services/mail/listmonk.nix b/nixos/modules/services/mail/listmonk.nix
index 11b2a518622..cea1bc95608 100644
--- a/nixos/modules/services/mail/listmonk.nix
+++ b/nixos/modules/services/mail/listmonk.nix
@@ -168,7 +168,7 @@ in {
 
       ensureUsers = [{
         name = "listmonk";
-        ensurePermissions = { "DATABASE listmonk" = "ALL PRIVILEGES"; };
+        ensureDBOwnership = true;
       }];
 
       ensureDatabases = [ "listmonk" ];
diff --git a/nixos/modules/services/mail/roundcube.nix b/nixos/modules/services/mail/roundcube.nix
index 22a4e3c451a..4e29f567ed9 100644
--- a/nixos/modules/services/mail/roundcube.nix
+++ b/nixos/modules/services/mail/roundcube.nix
@@ -179,14 +179,22 @@ in
       };
     };
 
+    assertions = [
+      {
+        assertion = localDB -> cfg.database.username == cfg.database.dbname;
+        message = ''
+          When setting up a DB and its owner user, the owner and the DB name must be
+          equal!
+        '';
+      }
+    ];
+
     services.postgresql = mkIf localDB {
       enable = true;
       ensureDatabases = [ cfg.database.dbname ];
       ensureUsers = [ {
         name = cfg.database.username;
-        ensurePermissions = {
-          "DATABASE ${cfg.database.username}" = "ALL PRIVILEGES";
-        };
+        ensureDBOwnership = true;
       } ];
     };
 
diff --git a/nixos/modules/services/mail/sympa.nix b/nixos/modules/services/mail/sympa.nix
index 7a5047b2bea..04ae46f66ee 100644
--- a/nixos/modules/services/mail/sympa.nix
+++ b/nixos/modules/services/mail/sympa.nix
@@ -218,7 +218,7 @@ in
         default = null;
         example = "/run/keys/sympa-dbpassword";
         description = lib.mdDoc ''
-          A file containing the password for {option}`services.sympa.database.user`.
+          A file containing the password for {option}`services.sympa.database.name`.
         '';
       };
 
@@ -342,6 +342,7 @@ in
 
       db_type = cfg.database.type;
       db_name = cfg.database.name;
+      db_user = cfg.database.name;
     }
     // (optionalAttrs (cfg.database.host != null) {
       db_host = cfg.database.host;
@@ -355,9 +356,6 @@ in
     // (optionalAttrs (cfg.database.port != null) {
       db_port = cfg.database.port;
     })
-    // (optionalAttrs (cfg.database.user != null) {
-      db_user = cfg.database.user;
-    })
     // (optionalAttrs (cfg.mta.type == "postfix") {
       sendmail_aliases = "${dataDir}/sympa_transport";
       aliases_program  = "${pkgs.postfix}/bin/postmap";
@@ -393,7 +391,7 @@ in
     users.groups.${group} = {};
 
     assertions = [
-      { assertion = cfg.database.createLocally -> cfg.database.user == user;
+      { assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.name == cfg.database.user;
         message = "services.sympa.database.user must be set to ${user} if services.sympa.database.createLocally is set to true";
       }
       { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
@@ -579,7 +577,7 @@ in
       ensureDatabases = [ cfg.database.name ];
       ensureUsers = [
         { name = cfg.database.user;
-          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/matrix/matrix-sliding-sync.nix b/nixos/modules/services/matrix/matrix-sliding-sync.nix
index 9807cde4091..84bb38f35ae 100644
--- a/nixos/modules/services/matrix/matrix-sliding-sync.nix
+++ b/nixos/modules/services/matrix/matrix-sliding-sync.nix
@@ -74,9 +74,9 @@ in
     services.postgresql = lib.optionalAttrs cfg.createDatabase {
       enable = true;
       ensureDatabases = [ "matrix-sliding-sync" ];
-      ensureUsers = [ rec {
+      ensureUsers = [ {
         name = "matrix-sliding-sync";
-        ensurePermissions."DATABASE \"${name}\"" = "ALL PRIVILEGES";
+        ensureDBOwnership = true;
       } ];
     };
 
diff --git a/nixos/modules/services/matrix/mautrix-facebook.nix b/nixos/modules/services/matrix/mautrix-facebook.nix
index 671040500df..d7cf024bb80 100644
--- a/nixos/modules/services/matrix/mautrix-facebook.nix
+++ b/nixos/modules/services/matrix/mautrix-facebook.nix
@@ -135,9 +135,7 @@ in {
       ensureDatabases = ["mautrix-facebook"];
       ensureUsers = [{
         name = "mautrix-facebook";
-        ensurePermissions = {
-          "DATABASE \"mautrix-facebook\"" = "ALL PRIVILEGES";
-        };
+        ensureDBOwnership = true;
       }];
     };
 
diff --git a/nixos/modules/services/misc/atuin.nix b/nixos/modules/services/misc/atuin.nix
index 8d2c1b5242f..2d6ffc510ce 100644
--- a/nixos/modules/services/misc/atuin.nix
+++ b/nixos/modules/services/misc/atuin.nix
@@ -73,9 +73,7 @@ in
       enable = true;
       ensureUsers = [{
         name = "atuin";
-        ensurePermissions = {
-          "DATABASE atuin" = "ALL PRIVILEGES";
-        };
+        ensureDBOwnership = true;
       }];
       ensureDatabases = [ "atuin" ];
     };
diff --git a/nixos/modules/services/misc/forgejo.nix b/nixos/modules/services/misc/forgejo.nix
index 90b5f16f418..6f459048f34 100644
--- a/nixos/modules/services/misc/forgejo.nix
+++ b/nixos/modules/services/misc/forgejo.nix
@@ -357,6 +357,14 @@ in
         assertion = cfg.database.createDatabase -> useSqlite || cfg.database.user == cfg.user;
         message = "services.forgejo.database.user must match services.forgejo.user if the database is to be automatically provisioned";
       }
+      { assertion = cfg.database.createDatabase && usePostgresql -> cfg.database.user == cfg.database.name;
+        message = ''
+          When creating a database via NixOS, the db user and db name must be equal!
+          If you already have an existing DB+user and this assertion is new, you can safely set
+          `services.forgejo.createDatabase` to `false` because removal of `ensureUsers`
+          and `ensureDatabases` doesn't have any effect.
+        '';
+      }
     ];
 
     services.forgejo.settings = {
@@ -423,7 +431,7 @@ in
       ensureUsers = [
         {
           name = cfg.database.user;
-          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix
index 3f690f85d62..be528a29899 100644
--- a/nixos/modules/services/misc/gitea.nix
+++ b/nixos/modules/services/misc/gitea.nix
@@ -394,6 +394,14 @@ in
       { assertion = cfg.database.createDatabase -> useSqlite || cfg.database.user == cfg.user;
         message = "services.gitea.database.user must match services.gitea.user if the database is to be automatically provisioned";
       }
+      { assertion = cfg.database.createDatabase && usePostgresql -> cfg.database.user == cfg.database.name;
+        message = ''
+          When creating a database via NixOS, the db user and db name must be equal!
+          If you already have an existing DB+user and this assertion is new, you can safely set
+          `services.gitea.createDatabase` to `false` because removal of `ensureUsers`
+          and `ensureDatabases` doesn't have any effect.
+        '';
+      }
     ];
 
     services.gitea.settings = {
@@ -461,7 +469,7 @@ in
       ensureDatabases = [ cfg.database.name ];
       ensureUsers = [
         { name = cfg.database.user;
-          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/misc/redmine.nix b/nixos/modules/services/misc/redmine.nix
index a296fd3816b..20fa71507b6 100644
--- a/nixos/modules/services/misc/redmine.nix
+++ b/nixos/modules/services/misc/redmine.nix
@@ -267,7 +267,7 @@ in
       { assertion = cfg.database.passwordFile != null || cfg.database.socket != null;
         message = "one of services.redmine.database.socket or services.redmine.database.passwordFile must be set";
       }
-      { assertion = cfg.database.createLocally -> cfg.database.user == cfg.user;
+      { assertion = cfg.database.createLocally -> cfg.database.user == cfg.user && cfg.database.user == cfg.database.name;
         message = "services.redmine.database.user must be set to ${cfg.user} if services.redmine.database.createLocally is set true";
       }
       { assertion = cfg.database.createLocally -> cfg.database.socket != null;
@@ -315,7 +315,7 @@ in
       ensureDatabases = [ cfg.database.name ];
       ensureUsers = [
         { name = cfg.database.user;
-          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/misc/sourcehut/service.nix b/nixos/modules/services/misc/sourcehut/service.nix
index 18c2f5effc5..d2cd599d3fe 100644
--- a/nixos/modules/services/misc/sourcehut/service.nix
+++ b/nixos/modules/services/misc/sourcehut/service.nix
@@ -242,6 +242,15 @@ in
       } cfg.nginx.virtualHost ];
     };
 
+    assertions = [
+      {
+        assertion = srvCfg.user == srvCfg.postgresql.database;
+        message = ''
+          When creating a database via NixOS, the db user and db name must be equal!
+        '';
+      }
+    ];
+
     services.postgresql = mkIf cfg.postgresql.enable {
       authentication = ''
         local ${srvCfg.postgresql.database} ${srvCfg.user} trust
@@ -249,7 +258,7 @@ in
       ensureDatabases = [ srvCfg.postgresql.database ];
       ensureUsers = map (name: {
           inherit name;
-          ensurePermissions = { "DATABASE \"${srvCfg.postgresql.database}\"" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }) [srvCfg.user];
     };
 
diff --git a/nixos/modules/services/monitoring/zabbix-proxy.nix b/nixos/modules/services/monitoring/zabbix-proxy.nix
index 85da416ba6c..503e81b48a5 100644
--- a/nixos/modules/services/monitoring/zabbix-proxy.nix
+++ b/nixos/modules/services/monitoring/zabbix-proxy.nix
@@ -203,7 +203,7 @@ in
       { assertion = !config.services.zabbixServer.enable;
         message = "Please choose one of services.zabbixServer or services.zabbixProxy.";
       }
-      { assertion = cfg.database.createLocally -> cfg.database.user == user;
+      { assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.name == cfg.database.user;
         message = "services.zabbixProxy.database.user must be set to ${user} if services.zabbixProxy.database.createLocally is set true";
       }
       { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
@@ -252,7 +252,7 @@ in
       ensureDatabases = [ cfg.database.name ];
       ensureUsers = [
         { name = cfg.database.user;
-          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/monitoring/zabbix-server.nix b/nixos/modules/services/monitoring/zabbix-server.nix
index 2b50280e396..0607188d213 100644
--- a/nixos/modules/services/monitoring/zabbix-server.nix
+++ b/nixos/modules/services/monitoring/zabbix-server.nix
@@ -191,7 +191,7 @@ in
   config = mkIf cfg.enable {
 
     assertions = [
-      { assertion = cfg.database.createLocally -> cfg.database.user == user;
+      { assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.user == cfg.database.name;
         message = "services.zabbixServer.database.user must be set to ${user} if services.zabbixServer.database.createLocally is set true";
       }
       { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
@@ -240,7 +240,7 @@ in
       ensureDatabases = [ cfg.database.name ];
       ensureUsers = [
         { name = cfg.database.user;
-          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/security/hockeypuck.nix b/nixos/modules/services/security/hockeypuck.nix
index 127134bc5db..56c13d79192 100644
--- a/nixos/modules/services/security/hockeypuck.nix
+++ b/nixos/modules/services/security/hockeypuck.nix
@@ -55,7 +55,7 @@ in {
             ensureDatabases = [ "hockeypuck" ];
             ensureUsers = [{
               name = "hockeypuck";
-              ensurePermissions."DATABASE hockeypuck" = "ALL PRIVILEGES";
+              ensureDBOwnership = true;
             }];
           };
         ```
diff --git a/nixos/modules/services/web-apps/coder.nix b/nixos/modules/services/web-apps/coder.nix
index 469a29bc3aa..f65211308c4 100644
--- a/nixos/modules/services/web-apps/coder.nix
+++ b/nixos/modules/services/web-apps/coder.nix
@@ -149,8 +149,8 @@ in {
 
   config = mkIf cfg.enable {
     assertions = [
-      { assertion = cfg.database.createLocally -> cfg.database.username == name;
-        message = "services.coder.database.username must be set to ${user} if services.coder.database.createLocally is set true";
+      { assertion = cfg.database.createLocally -> cfg.database.username == name && cfg.database.database == cfg.database.username;
+        message = "services.coder.database.username must be set to ${name} if services.coder.database.createLocally is set true";
       }
     ];
 
@@ -193,10 +193,8 @@ in {
         cfg.database.database
       ];
       ensureUsers = [{
-        name = cfg.database.username;
-        ensurePermissions = {
-          "DATABASE \"${cfg.database.database}\"" = "ALL PRIVILEGES";
-        };
+        name = cfg.user;
+        ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/web-apps/gotosocial.nix b/nixos/modules/services/web-apps/gotosocial.nix
index f7ae018d5b7..9c21719a575 100644
--- a/nixos/modules/services/web-apps/gotosocial.nix
+++ b/nixos/modules/services/web-apps/gotosocial.nix
@@ -128,9 +128,7 @@ in
       ensureUsers = [
         {
           name = "gotosocial";
-          ensurePermissions = {
-            "DATABASE gotosocial" = "ALL PRIVILEGES";
-          };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/web-apps/invidious.nix b/nixos/modules/services/web-apps/invidious.nix
index 5603ef7392e..fc9c1ec06f6 100644
--- a/nixos/modules/services/web-apps/invidious.nix
+++ b/nixos/modules/services/web-apps/invidious.nix
@@ -112,12 +112,6 @@ let
     services.postgresql = {
       enable = true;
       ensureDatabases = lib.singleton cfg.settings.db.dbname;
-      ensureUsers = lib.singleton {
-        name = cfg.settings.db.user;
-        ensurePermissions = {
-          "DATABASE ${cfg.settings.db.dbname}" = "ALL PRIVILEGES";
-        };
-      };
       # This is only needed because the unix user invidious isn't the same as
       # the database user. This tells postgres to map one to the other.
       identMap = ''
diff --git a/nixos/modules/services/web-apps/lemmy.nix b/nixos/modules/services/web-apps/lemmy.nix
index 20d9dcb7c26..32389f7a59d 100644
--- a/nixos/modules/services/web-apps/lemmy.nix
+++ b/nixos/modules/services/web-apps/lemmy.nix
@@ -146,7 +146,7 @@ in
         ensureDatabases = [ cfg.settings.database.database ];
         ensureUsers = [{
           name = cfg.settings.database.user;
-          ensurePermissions."DATABASE ${cfg.settings.database.database}" = "ALL PRIVILEGES";
+          ensureDBOwnership = true;
         }];
       };
 
diff --git a/nixos/modules/services/web-apps/mastodon.nix b/nixos/modules/services/web-apps/mastodon.nix
index 2aab97438b7..5feca2525ea 100644
--- a/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixos/modules/services/web-apps/mastodon.nix
@@ -557,7 +557,7 @@ in {
   config = lib.mkIf cfg.enable (lib.mkMerge [{
     assertions = [
       {
-        assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.database.user);
+        assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.database.user && cfg.database.user == cfg.database.name);
         message = ''
           For local automatic database provisioning (services.mastodon.database.createLocally == true) with peer
             authentication (services.mastodon.database.host == "/run/postgresql") to work services.mastodon.user
@@ -799,8 +799,8 @@ in {
       enable = true;
       ensureUsers = [
         {
-          name = cfg.database.user;
-          ensurePermissions."DATABASE ${cfg.database.name}" = "ALL PRIVILEGES";
+          name = cfg.database.name;
+          ensureDBOwnership = true;
         }
       ];
       ensureDatabases = [ cfg.database.name ];
diff --git a/nixos/modules/services/web-apps/mediawiki.nix b/nixos/modules/services/web-apps/mediawiki.nix
index 8b494b7c120..ce7bcd94b3f 100644
--- a/nixos/modules/services/web-apps/mediawiki.nix
+++ b/nixos/modules/services/web-apps/mediawiki.nix
@@ -454,7 +454,7 @@ in
       { assertion = cfg.database.createLocally -> (cfg.database.type == "mysql" || cfg.database.type == "postgres");
         message = "services.mediawiki.createLocally is currently only supported for database type 'mysql' and 'postgres'";
       }
-      { assertion = cfg.database.createLocally -> cfg.database.user == user;
+      { assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.name == cfg.database.user;
         message = "services.mediawiki.database.user must be set to ${user} if services.mediawiki.database.createLocally is set true";
       }
       { assertion = cfg.database.createLocally -> cfg.database.socket != null;
@@ -486,7 +486,7 @@ in
       ensureDatabases = [ cfg.database.name ];
       ensureUsers = [{
         name = cfg.database.user;
-        ensurePermissions = { "DATABASE \"${cfg.database.name}\"" = "ALL PRIVILEGES"; };
+        ensureDBOwnership = true;
       }];
     };
 
diff --git a/nixos/modules/services/web-apps/miniflux.nix b/nixos/modules/services/web-apps/miniflux.nix
index 3374c746ad3..5c8c93c13c4 100644
--- a/nixos/modules/services/web-apps/miniflux.nix
+++ b/nixos/modules/services/web-apps/miniflux.nix
@@ -6,13 +6,10 @@ let
 
   defaultAddress = "localhost:8080";
 
-  dbUser = "miniflux";
-  dbName = "miniflux";
-
   pgbin = "${config.services.postgresql.package}/bin";
   preStart = pkgs.writeScript "miniflux-pre-start" ''
     #!${pkgs.runtimeShell}
-    ${pgbin}/psql "${dbName}" -c "CREATE EXTENSION IF NOT EXISTS hstore"
+    ${pgbin}/psql "miniflux" -c "CREATE EXTENSION IF NOT EXISTS hstore"
   '';
 in
 
@@ -62,7 +59,7 @@ in
 
     services.miniflux.config =  {
       LISTEN_ADDR = mkDefault defaultAddress;
-      DATABASE_URL = "user=${dbUser} host=/run/postgresql dbname=${dbName}";
+      DATABASE_URL = "user=miniflux host=/run/postgresql dbname=miniflux";
       RUN_MIGRATIONS = "1";
       CREATE_ADMIN = "1";
     };
@@ -70,12 +67,10 @@ in
     services.postgresql = {
       enable = true;
       ensureUsers = [ {
-        name = dbUser;
-        ensurePermissions = {
-          "DATABASE ${dbName}" = "ALL PRIVILEGES";
-        };
+        name = "miniflux";
+        ensureDBOwnership = true;
       } ];
-      ensureDatabases = [ dbName ];
+      ensureDatabases = [ "miniflux" ];
     };
 
     systemd.services.miniflux-dbsetup = {
@@ -97,7 +92,7 @@ in
 
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/miniflux";
-        User = dbUser;
+        User = "miniflux";
         DynamicUser = true;
         RuntimeDirectory = "miniflux";
         RuntimeDirectoryMode = "0700";
diff --git a/nixos/modules/services/web-apps/mobilizon.nix b/nixos/modules/services/web-apps/mobilizon.nix
index 343c5cead2b..601c2830e0e 100644
--- a/nixos/modules/services/web-apps/mobilizon.nix
+++ b/nixos/modules/services/web-apps/mobilizon.nix
@@ -212,6 +212,12 @@ in
         assertion = cfg.nginx.enable -> (cfg.settings.":mobilizon"."Mobilizon.Web.Endpoint".http.ip == settingsFormat.lib.mkTuple [ 0 0 0 0 0 0 0 1 ]);
         message = "Setting the IP mobilizon listens on is only possible when the nginx config is not used, as it is hardcoded there.";
       }
+      {
+        assertion = isLocalPostgres -> repoSettings.database == repoSettings.username;
+        message = ''
+          When creating a database via NixOS, the db user and db name must be equal!
+        '';
+      }
     ];
 
     services.mobilizon.settings = {
@@ -372,9 +378,7 @@ in
       ensureUsers = [
         {
           name = dbUser;
-          ensurePermissions = {
-            "DATABASE \"${repoSettings.database}\"" = "ALL PRIVILEGES";
-          };
+          ensureDBOwnership = true;
         }
       ];
       extraPlugins = with postgresql.pkgs; [ postgis ];
diff --git a/nixos/modules/services/web-apps/moodle.nix b/nixos/modules/services/web-apps/moodle.nix
index b617e9a5937..04ae6bd7f17 100644
--- a/nixos/modules/services/web-apps/moodle.nix
+++ b/nixos/modules/services/web-apps/moodle.nix
@@ -194,7 +194,7 @@ in
   config = mkIf cfg.enable {
 
     assertions = [
-      { assertion = cfg.database.createLocally -> cfg.database.user == user;
+      { assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.user == cfg.database.name;
         message = "services.moodle.database.user must be set to ${user} if services.moodle.database.createLocally is set true";
       }
       { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
@@ -220,7 +220,7 @@ in
       ensureDatabases = [ cfg.database.name ];
       ensureUsers = [
         { name = cfg.database.user;
-          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/web-apps/netbox.nix b/nixos/modules/services/web-apps/netbox.nix
index 8ba1852848e..3b9434e3d34 100644
--- a/nixos/modules/services/web-apps/netbox.nix
+++ b/nixos/modules/services/web-apps/netbox.nix
@@ -257,9 +257,7 @@ in {
       ensureUsers = [
         {
           name = "netbox";
-          ensurePermissions = {
-            "DATABASE netbox" = "ALL PRIVILEGES";
-          };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index f9713cac47e..f1ac3770d40 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -1042,7 +1042,7 @@ in {
         ensureDatabases = [ cfg.config.dbname ];
         ensureUsers = [{
           name = cfg.config.dbuser;
-          ensurePermissions = { "DATABASE ${cfg.config.dbname}" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }];
       };
 
diff --git a/nixos/modules/services/web-apps/onlyoffice.nix b/nixos/modules/services/web-apps/onlyoffice.nix
index 3494f2fa21f..f958566b91f 100644
--- a/nixos/modules/services/web-apps/onlyoffice.nix
+++ b/nixos/modules/services/web-apps/onlyoffice.nix
@@ -198,7 +198,7 @@ in
         ensureDatabases = [ "onlyoffice" ];
         ensureUsers = [{
           name = "onlyoffice";
-          ensurePermissions = { "DATABASE \"onlyoffice\"" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }];
       };
     };
diff --git a/nixos/modules/services/web-apps/outline.nix b/nixos/modules/services/web-apps/outline.nix
index 0e3bd07c1fc..d97b45d6241 100644
--- a/nixos/modules/services/web-apps/outline.nix
+++ b/nixos/modules/services/web-apps/outline.nix
@@ -581,7 +581,7 @@ in
       enable = true;
       ensureUsers = [{
         name = "outline";
-        ensurePermissions."DATABASE outline" = "ALL PRIVILEGES";
+        ensureDBOwnership = true;
       }];
       ensureDatabases = [ "outline" ];
     };
diff --git a/nixos/modules/services/web-apps/peering-manager.nix b/nixos/modules/services/web-apps/peering-manager.nix
index 7012df6dffb..d6f6077268d 100644
--- a/nixos/modules/services/web-apps/peering-manager.nix
+++ b/nixos/modules/services/web-apps/peering-manager.nix
@@ -186,9 +186,7 @@ in {
       ensureUsers = [
         {
           name = "peering-manager";
-          ensurePermissions = {
-            "DATABASE \"peering-manager\"" = "ALL PRIVILEGES";
-          };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/web-apps/pixelfed.nix b/nixos/modules/services/web-apps/pixelfed.nix
index 159fb52476a..b0a25dcce9e 100644
--- a/nixos/modules/services/web-apps/pixelfed.nix
+++ b/nixos/modules/services/web-apps/pixelfed.nix
@@ -271,7 +271,6 @@ in {
         ensureDatabases = [ cfg.database.name ];
         ensureUsers = [{
           name = user;
-          ensurePermissions = { };
         }];
       };
 
diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix
index 7b2e3be4295..a8fb37d2c5e 100644
--- a/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixos/modules/services/web-apps/tt-rss.nix
@@ -529,6 +529,15 @@ let
         assertion = cfg.database.password != null -> cfg.database.passwordFile == null;
         message = "Cannot set both password and passwordFile";
       }
+      {
+        assertion = cfg.database.createLocally -> cfg.database.name == cfg.user && cfg.database.user == cfg.user;
+        message = ''
+          When creating a database via NixOS, the db user and db name must be equal!
+          If you already have an existing DB+user and this assertion is new, you can safely set
+          `services.tt-rss.database.createLocally` to `false` because removal of `ensureUsers`
+          and `ensureDatabases` doesn't have any effect.
+        '';
+      }
     ];
 
     services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") {
@@ -632,8 +641,8 @@ let
       enable = mkDefault true;
       ensureDatabases = [ cfg.database.name ];
       ensureUsers = [
-        { name = cfg.user;
-          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+        { name = cfg.database.user;
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/modules/services/web-servers/hydron.nix b/nixos/modules/services/web-servers/hydron.nix
index 4434965b217..9d30fdc0caa 100644
--- a/nixos/modules/services/web-servers/hydron.nix
+++ b/nixos/modules/services/web-servers/hydron.nix
@@ -93,7 +93,7 @@ in with lib; {
       ensureDatabases = [ "hydron" ];
       ensureUsers = [
         { name = "hydron";
-          ensurePermissions = { "DATABASE hydron" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/tests/dex-oidc.nix b/nixos/tests/dex-oidc.nix
index 37275a97ef0..e54ae18ca93 100644
--- a/nixos/tests/dex-oidc.nix
+++ b/nixos/tests/dex-oidc.nix
@@ -49,7 +49,7 @@ import ./make-test-python.nix ({ lib, ... }: {
       ensureUsers = [
         {
           name = "dex";
-          ensurePermissions = { "DATABASE dex" = "ALL PRIVILEGES"; };
+          ensureDBOwnership = true;
         }
       ];
     };
diff --git a/nixos/tests/ferretdb.nix b/nixos/tests/ferretdb.nix
index 9ad7397ade8..7251198af77 100644
--- a/nixos/tests/ferretdb.nix
+++ b/nixos/tests/ferretdb.nix
@@ -39,7 +39,7 @@ with import ../lib/testing-python.nix { inherit system; };
             ensureDatabases = [ "ferretdb" ];
             ensureUsers = [{
               name = "ferretdb";
-              ensurePermissions."DATABASE ferretdb" = "ALL PRIVILEGES";
+              ensureDBOwnership = true;
             }];
           };
 
diff --git a/nixos/tests/freshrss-pgsql.nix b/nixos/tests/freshrss-pgsql.nix
index 055bd51ed43..c685f4a8159 100644
--- a/nixos/tests/freshrss-pgsql.nix
+++ b/nixos/tests/freshrss-pgsql.nix
@@ -22,9 +22,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
       ensureUsers = [
         {
           name = "freshrss";
-          ensurePermissions = {
-            "DATABASE freshrss" = "ALL PRIVILEGES";
-          };
+          ensureDBOwnership = true;
         }
       ];
       initialScript = pkgs.writeText "postgresql-password" ''
diff --git a/nixos/tests/grafana/basic.nix b/nixos/tests/grafana/basic.nix
index 8bf4caad7fb..dd389bc8a3d 100644
--- a/nixos/tests/grafana/basic.nix
+++ b/nixos/tests/grafana/basic.nix
@@ -55,7 +55,7 @@ let
         ensureDatabases = [ "grafana" ];
         ensureUsers = [{
           name = "grafana";
-          ensurePermissions."DATABASE grafana" = "ALL PRIVILEGES";
+          ensureDBOwnership = true;
         }];
       };
       systemd.services.grafana.after = [ "postgresql.service" ];
diff --git a/nixos/tests/hockeypuck.nix b/nixos/tests/hockeypuck.nix
index 2b9dba8720a..675d6b226ad 100644
--- a/nixos/tests/hockeypuck.nix
+++ b/nixos/tests/hockeypuck.nix
@@ -35,7 +35,7 @@ in {
       ensureDatabases = [ "hockeypuck" ];
       ensureUsers = [{
         name = "hockeypuck";
-        ensurePermissions."DATABASE hockeypuck" = "ALL PRIVILEGES";
+        ensureDBOwnership = true;
       }];
     };
   };
diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix
index e97e8a467b1..e1588088ba1 100644
--- a/nixos/tests/home-assistant.nix
+++ b/nixos/tests/home-assistant.nix
@@ -9,13 +9,11 @@ in {
   nodes.hass = { pkgs, ... }: {
     services.postgresql = {
       enable = true;
-
-      # FIXME: hack for https://github.com/NixOS/nixpkgs/issues/216989
-      # Should be replaced with ensureUsers again when a solution for that is found
-      initialScript = pkgs.writeText "hass-setup-db.sql" ''
-        CREATE ROLE hass WITH LOGIN;
-        CREATE DATABASE hass WITH OWNER hass;
-      '';
+      ensureDatabases = [ "hass" ];
+      ensureUsers = [{
+        name = "hass";
+        ensureDBOwnership = true;
+      }];
     };
 
     services.home-assistant = {
diff --git a/nixos/tests/paperless.nix b/nixos/tests/paperless.nix
index 22409e89923..6a51cc522bd 100644
--- a/nixos/tests/paperless.nix
+++ b/nixos/tests/paperless.nix
@@ -17,7 +17,7 @@ import ./make-test-python.nix ({ lib, ... }: {
         ensureDatabases = [ "paperless" ];
         ensureUsers = [
           { name = config.services.paperless.user;
-            ensurePermissions = { "DATABASE \"paperless\"" = "ALL PRIVILEGES"; };
+            ensureDBOwnership = true;
           }
         ];
       };
diff --git a/nixos/tests/pgadmin4.nix b/nixos/tests/pgadmin4.nix
index cb8de87c9ee..3ee7ed19fa1 100644
--- a/nixos/tests/pgadmin4.nix
+++ b/nixos/tests/pgadmin4.nix
@@ -19,14 +19,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
       authentication = ''
         host    all             all             localhost               trust
       '';
-      ensureUsers = [
-        {
-          name = "postgres";
-          ensurePermissions = {
-            "DATABASE \"postgres\"" = "ALL PRIVILEGES";
-          };
-        }
-      ];
     };
 
     services.pgadmin = {
diff --git a/nixos/tests/pgbouncer.nix b/nixos/tests/pgbouncer.nix
index 1e72327d420..814ca0d5886 100644
--- a/nixos/tests/pgbouncer.nix
+++ b/nixos/tests/pgbouncer.nix
@@ -24,13 +24,11 @@ in
       services = {
         postgresql = {
           enable = true;
-          ensureDatabases = [ "testdb" ];
+          ensureDatabases = [ "test" ];
           ensureUsers = [
           {
-            name = "testuser";
-            ensurePermissions = {
-              "DATABASE testdb" = "ALL PRIVILEGES";
-            };
+            name = "test";
+            ensureDBOwnership = true;
           }];
           authentication = ''
             local testdb testuser scram-sha-256
@@ -40,7 +38,7 @@ in
         pgbouncer = {
           enable = true;
           listenAddress = "localhost";
-          databases = { testdb = "host=/run/postgresql/ port=5432 auth_user=testuser dbname=testdb"; };
+          databases = { test = "host=/run/postgresql/ port=5432 auth_user=testuser dbname=test"; };
           authType = "scram-sha-256";
           authFile = testAuthFile;
         };
@@ -55,7 +53,7 @@ in
 
     # Test if we can make a query through PgBouncer
     one.wait_until_succeeds(
-        "psql 'postgres://testuser:testpass@localhost:6432/testdb' -c 'SELECT 1;'"
+        "psql 'postgres://testuser:testpass@localhost:6432/test' -c 'SELECT 1;'"
     )
   '';
 })
diff --git a/nixos/tests/powerdns-admin.nix b/nixos/tests/powerdns-admin.nix
index d7bacb24eec..d326d74a982 100644
--- a/nixos/tests/powerdns-admin.nix
+++ b/nixos/tests/powerdns-admin.nix
@@ -87,9 +87,7 @@ let
           ensureUsers = [
             {
               name = "powerdnsadmin";
-              ensurePermissions = {
-                "DATABASE powerdnsadmin" = "ALL PRIVILEGES";
-              };
+              ensureDBOwnership = true;
             }
           ];
         };
diff --git a/nixos/tests/sftpgo.nix b/nixos/tests/sftpgo.nix
index db0098d2ac4..a5bb1981d2c 100644
--- a/nixos/tests/sftpgo.nix
+++ b/nixos/tests/sftpgo.nix
@@ -156,7 +156,7 @@ in
         ensureDatabases = [ "sftpgo" ];
         ensureUsers = [{
           name = "sftpgo";
-          ensurePermissions."DATABASE sftpgo" = "ALL PRIVILEGES";
+          ensureDBOwnership = true;
         }];
       };
 
diff --git a/nixos/tests/tandoor-recipes.nix b/nixos/tests/tandoor-recipes.nix
index f3369da99a0..18beaac6f06 100644
--- a/nixos/tests/tandoor-recipes.nix
+++ b/nixos/tests/tandoor-recipes.nix
@@ -5,6 +5,29 @@ import ./make-test-python.nix ({ lib, ... }: {
   nodes.machine = { pkgs, ... }: {
     services.tandoor-recipes = {
       enable = true;
+      extraConfig = {
+        DB_ENGINE = "django.db.backends.postgresql";
+        POSTGRES_HOST = "/run/postgresql";
+        POSTGRES_USER = "tandoor_recipes";
+        POSTGRES_DB = "tandoor_recipes";
+      };
+    };
+
+    services.postgresql = {
+      enable = true;
+      ensureDatabases = [ "tandoor_recipes" ];
+      ensureUsers = [
+        {
+          name = "tandoor_recipes";
+          ensureDBOwnership = true;
+        }
+      ];
+    };
+
+    systemd.services = {
+      tandoor-recipes = {
+        after = [ "postgresql.service" ];
+      };
     };
   };
 
diff --git a/nixos/tests/vikunja.nix b/nixos/tests/vikunja.nix
index 2660aa9767c..60fd5ce1385 100644
--- a/nixos/tests/vikunja.nix
+++ b/nixos/tests/vikunja.nix
@@ -33,7 +33,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
         ensureDatabases = [ "vikunja-api" ];
         ensureUsers = [
           { name = "vikunja-api";
-            ensurePermissions = { "DATABASE \"vikunja-api\"" = "ALL PRIVILEGES"; };
+            ensureDBOwnership = true;
           }
         ];
       };
diff --git a/nixos/tests/wiki-js.nix b/nixos/tests/wiki-js.nix
index fd054a9c590..8b3c51935a6 100644
--- a/nixos/tests/wiki-js.nix
+++ b/nixos/tests/wiki-js.nix
@@ -10,14 +10,15 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
       enable = true;
       settings.db.host = "/run/postgresql";
       settings.db.user = "wiki-js";
+      settings.db.db = "wiki-js";
       settings.logLevel = "debug";
     };
     services.postgresql = {
       enable = true;
-      ensureDatabases = [ "wiki" ];
+      ensureDatabases = [ "wiki-js" ];
       ensureUsers = [
         { name = "wiki-js";
-          ensurePermissions."DATABASE wiki" = "ALL PRIVILEGES";
+          ensureDBOwnership = true;
         }
       ];
     };