···145145146146## Backward Incompatibilities {#sec-release-23.11-incompatibilities}
147147148148+- `services.postgresql.ensurePermissions` has been deprecated in favor of `services.postgresql.ensureUsers.*.ensureDBOwnership` which simplifies the setup of database owned by a certain system user
149149+ in local database contexts (which make use of peer authentication via UNIX sockets), migration guidelines were provided in the NixOS manual, please refer to them if you are affected by a PostgreSQL 15 changing the way `GRANT ALL PRIVILEGES` is working. `services.postgresql.ensurePermissions` will be removed in 24.05. All NixOS modules were migrated using one of the strategy, e.g. `ensureDBOwnership` or `postStart`. More about this situation can be learnt in https://github.com/NixOS/nixpkgs/pull/266270.
150150+148151- `network-online.target` has been fixed to no longer time out for systems with `networking.useDHCP = true` and `networking.useNetworkd = true`.
149152 Workarounds for this can be removed.
150153
+47-11
nixos/modules/services/databases/postgresql.nix
···168168 ensurePermissions = mkOption {
169169 type = types.attrsOf types.str;
170170 default = {};
171171+ visible = false; # This option has been deprecated.
171172 description = lib.mdDoc ''
173173+ This option is DEPRECATED and should not be used in nixpkgs anymore,
174174+ use `ensureDBOwnership` instead. It can also break with newer
175175+ versions of PostgreSQL (≥ 15).
176176+172177 Permissions to ensure for the user, specified as an attribute set.
173178 The attribute names specify the database and tables to grant the permissions for.
174179 The attribute values specify the permissions to grant. You may specify one or
···184189 "DATABASE \"nextcloud\"" = "ALL PRIVILEGES";
185190 "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
186191 }
192192+ '';
193193+ };
194194+195195+ ensureDBOwnership = mkOption {
196196+ type = types.bool;
197197+ default = false;
198198+ description = mdDoc ''
199199+ Grants the user ownership to a database with the same name.
200200+ This database must be defined manually in
201201+ [](#opt-services.postgresql.ensureDatabases).
187202 '';
188203 };
189204···338353 });
339354 default = [];
340355 description = lib.mdDoc ''
341341- Ensures that the specified users exist and have at least the ensured permissions.
356356+ Ensures that the specified users exist.
342357 The PostgreSQL users will be identified using peer authentication. This authenticates the Unix user with the
343358 same name only, and that without the need for a password.
344344- This option will never delete existing users or remove permissions, especially not when the value of this
345345- option is changed. This means that users created and permissions assigned once through this option or
346346- otherwise have to be removed manually.
359359+ This option will never delete existing users or remove DB ownership of databases
360360+ once granted with `ensureDBOwnership = true;`. This means that this must be
361361+ cleaned up manually when changing after changing the config in here.
347362 '';
348363 example = literalExpression ''
349364 [
350365 {
351366 name = "nextcloud";
352352- ensurePermissions = {
353353- "DATABASE nextcloud" = "ALL PRIVILEGES";
354354- };
355367 }
356368 {
357369 name = "superuser";
358358- ensurePermissions = {
359359- "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
360360- };
370370+ ensureDBOwnership = true;
361371 }
362372 ]
363373 '';
···445455446456 config = mkIf cfg.enable {
447457458458+ assertions = map ({ name, ensureDBOwnership, ... }: {
459459+ assertion = ensureDBOwnership -> builtins.elem name cfg.ensureDatabases;
460460+ message = ''
461461+ For each database user defined with `services.postgresql.ensureUsers` and
462462+ `ensureDBOwnership = true;`, a database with the same name must be defined
463463+ in `services.postgresql.ensureDatabases`.
464464+465465+ Offender: ${name} has not been found among databases.
466466+ '';
467467+ }) cfg.ensureUsers;
468468+ # `ensurePermissions` is now deprecated, let's avoid it.
469469+ warnings = lib.optional (any ({ ensurePermissions, ... }: ensurePermissions != {}) cfg.ensureUsers) "
470470+ `services.postgresql.*.ensurePermissions` is used in your expressions,
471471+ this option is known to be broken with newer PostgreSQL versions,
472472+ consider migrating to `services.postgresql.*.ensureDBOwnership` or
473473+ consult the release notes or manual for more migration guidelines.
474474+475475+ This option will be removed in NixOS 24.05 unless it sees significant
476476+ maintenance improvements.
477477+ ";
478478+448479 services.postgresql.settings =
449480 {
450481 hba_file = "${pkgs.writeText "pg_hba.conf" cfg.authentication}";
···556587 ${
557588 concatMapStrings
558589 (user:
559559- let
590590+ let
560591 userPermissions = concatStringsSep "\n"
561592 (mapAttrsToList
562593 (database: permission: ''$PSQL -tAc 'GRANT ${permission} ON ${database} TO "${user.name}"' '')
563594 user.ensurePermissions
564595 );
596596+ dbOwnershipStmt = optionalString
597597+ user.ensureDBOwnership
598598+ ''$PSQL -tAc 'ALTER DATABASE "${user.name}" OWNER TO "${user.name}";' '';
565599566600 filteredClauses = filterAttrs (name: value: value != null) user.ensureClauses;
567601···572606 $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"'
573607 ${userPermissions}
574608 ${userClauses}
609609+610610+ ${dbOwnershipStmt}
575611 ''
576612 )
577613 cfg.ensureUsers
+2-2
nixos/modules/services/development/zammad.nix
···204204205205 assertions = [
206206 {
207207- assertion = cfg.database.createLocally -> cfg.database.user == "zammad";
207207+ assertion = cfg.database.createLocally -> cfg.database.user == "zammad" && cfg.database.name == "zammad";
208208 message = "services.zammad.database.user must be set to \"zammad\" if services.zammad.database.createLocally is set to true";
209209 }
210210 {
···231231 ensureUsers = [
232232 {
233233 name = cfg.database.user;
234234- ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
234234+ ensureDBOwnership = true;
235235 }
236236 ];
237237 };
···357357 assertion = cfg.database.createDatabase -> useSqlite || cfg.database.user == cfg.user;
358358 message = "services.forgejo.database.user must match services.forgejo.user if the database is to be automatically provisioned";
359359 }
360360+ { assertion = cfg.database.createDatabase && usePostgresql -> cfg.database.user == cfg.database.name;
361361+ message = ''
362362+ When creating a database via NixOS, the db user and db name must be equal!
363363+ If you already have an existing DB+user and this assertion is new, you can safely set
364364+ `services.forgejo.createDatabase` to `false` because removal of `ensureUsers`
365365+ and `ensureDatabases` doesn't have any effect.
366366+ '';
367367+ }
360368 ];
361369362370 services.forgejo.settings = {
···423431 ensureUsers = [
424432 {
425433 name = cfg.database.user;
426426- ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
434434+ ensureDBOwnership = true;
427435 }
428436 ];
429437 };
+9-1
nixos/modules/services/misc/gitea.nix
···394394 { assertion = cfg.database.createDatabase -> useSqlite || cfg.database.user == cfg.user;
395395 message = "services.gitea.database.user must match services.gitea.user if the database is to be automatically provisioned";
396396 }
397397+ { assertion = cfg.database.createDatabase && usePostgresql -> cfg.database.user == cfg.database.name;
398398+ message = ''
399399+ When creating a database via NixOS, the db user and db name must be equal!
400400+ If you already have an existing DB+user and this assertion is new, you can safely set
401401+ `services.gitea.createDatabase` to `false` because removal of `ensureUsers`
402402+ and `ensureDatabases` doesn't have any effect.
403403+ '';
404404+ }
397405 ];
398406399407 services.gitea.settings = {
···461469 ensureDatabases = [ cfg.database.name ];
462470 ensureUsers = [
463471 { name = cfg.database.user;
464464- ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
472472+ ensureDBOwnership = true;
465473 }
466474 ];
467475 };
+2-2
nixos/modules/services/misc/redmine.nix
···267267 { assertion = cfg.database.passwordFile != null || cfg.database.socket != null;
268268 message = "one of services.redmine.database.socket or services.redmine.database.passwordFile must be set";
269269 }
270270- { assertion = cfg.database.createLocally -> cfg.database.user == cfg.user;
270270+ { assertion = cfg.database.createLocally -> cfg.database.user == cfg.user && cfg.database.user == cfg.database.name;
271271 message = "services.redmine.database.user must be set to ${cfg.user} if services.redmine.database.createLocally is set true";
272272 }
273273 { assertion = cfg.database.createLocally -> cfg.database.socket != null;
···315315 ensureDatabases = [ cfg.database.name ];
316316 ensureUsers = [
317317 { name = cfg.database.user;
318318- ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
318318+ ensureDBOwnership = true;
319319 }
320320 ];
321321 };
+7-3
nixos/modules/services/misc/sourcehut/service.nix
···249249 ensureDatabases = [ srvCfg.postgresql.database ];
250250 ensureUsers = map (name: {
251251 inherit name;
252252- ensurePermissions = { "DATABASE \"${srvCfg.postgresql.database}\"" = "ALL PRIVILEGES"; };
252252+ # We don't use it because we have a special default database name with dots.
253253+ # TODO(for maintainers of sourcehut): migrate away from custom preStart script.
254254+ ensureDBOwnership = false;
253255 }) [srvCfg.user];
254256 };
257257+255258256259 services.sourcehut.settings = mkMerge [
257260 {
···378381 extraService
379382 ])) extraServices)
380383381381- # Work around 'pq: permission denied for schema public' with postgres v15, until a
382382- # solution for `services.postgresql.ensureUsers` is found.
384384+ # Work around 'pq: permission denied for schema public' with postgres v15.
383385 # See https://github.com/NixOS/nixpkgs/issues/216989
384386 # Workaround taken from nixos/forgejo: https://github.com/NixOS/nixpkgs/pull/262741
387387+ # TODO(to maintainers of sourcehut): please migrate away from this workaround
388388+ # by migrating away from database name defaults with dots.
385389 (lib.mkIf (
386390 cfg.postgresql.enable
387391 && lib.strings.versionAtLeast config.services.postgresql.package.version "15.0"
···109109 # Default to using the local database if we create it
110110 services.invidious.database.host = lib.mkDefault null;
111111112112+113113+ # TODO(raitobezarius to maintainers of invidious): I strongly advise to clean up the kemal specific
114114+ # thing for 24.05 and use `ensureDBOwnership`.
115115+ # See https://github.com/NixOS/nixpkgs/issues/216989
116116+ systemd.services.postgresql.postStart = lib.mkAfter ''
117117+ $PSQL -tAc 'ALTER DATABASE "${cfg.settings.db.dbname}" OWNER TO "${cfg.settings.db.user}";'
118118+ '';
112119 services.postgresql = {
113120 enable = true;
121121+ ensureUsers = lib.singleton { name = cfg.settings.db.user; ensureDBOwnership = false; };
114122 ensureDatabases = lib.singleton cfg.settings.db.dbname;
115115- ensureUsers = lib.singleton {
116116- name = cfg.settings.db.user;
117117- ensurePermissions = {
118118- "DATABASE ${cfg.settings.db.dbname}" = "ALL PRIVILEGES";
119119- };
120120- };
121123 # This is only needed because the unix user invidious isn't the same as
122124 # the database user. This tells postgres to map one to the other.
123125 identMap = ''
···136138 documentation = [ "https://docs.invidious.io/Database-Information-and-Maintenance.md" ];
137139 startAt = lib.mkDefault "weekly";
138140 path = [ config.services.postgresql.package ];
141141+ after = [ "postgresql.service" ];
139142 script = ''
140143 psql ${cfg.settings.db.dbname} ${cfg.settings.db.user} -c "DELETE FROM nonces * WHERE expire < current_timestamp"
141144 psql ${cfg.settings.db.dbname} ${cfg.settings.db.user} -c "TRUNCATE TABLE videos"
···347347348348 # Taken from here:
349349 # https://framagit.org/framasoft/mobilizon/-/blob/1.1.0/priv/templates/setup_db.eex
350350+ # TODO(to maintainers of mobilizon): the owner database alteration is necessary
351351+ # as PostgreSQL 15 changed their behaviors w.r.t. to privileges.
352352+ # See https://github.com/NixOS/nixpkgs/issues/216989 to get rid
353353+ # of that workaround.
350354 script =
351355 ''
352356 psql "${repoSettings.database}" -c "\
353357 CREATE EXTENSION IF NOT EXISTS postgis; \
354358 CREATE EXTENSION IF NOT EXISTS pg_trgm; \
355359 CREATE EXTENSION IF NOT EXISTS unaccent;"
360360+ psql -tAc 'ALTER DATABASE "${repoSettings.database}" OWNER TO "${dbUser}";'
361361+356362 '';
357363358364 serviceConfig = {
···372378 ensureUsers = [
373379 {
374380 name = dbUser;
375375- ensurePermissions = {
376376- "DATABASE \"${repoSettings.database}\"" = "ALL PRIVILEGES";
377377- };
381381+ # Given that `dbUser` is potentially arbitrarily custom, we will perform
382382+ # manual fixups in mobilizon-postgres.
383383+ # TODO(to maintainers of mobilizon): Feel free to simplify your setup by using `ensureDBOwnership`.
384384+ ensureDBOwnership = false;
378385 }
379386 ];
380387 extraPlugins = with postgresql.pkgs; [ postgis ];
+2-2
nixos/modules/services/web-apps/moodle.nix
···194194 config = mkIf cfg.enable {
195195196196 assertions = [
197197- { assertion = cfg.database.createLocally -> cfg.database.user == user;
197197+ { assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.user == cfg.database.name;
198198 message = "services.moodle.database.user must be set to ${user} if services.moodle.database.createLocally is set true";
199199 }
200200 { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
···220220 ensureDatabases = [ cfg.database.name ];
221221 ensureUsers = [
222222 { name = cfg.database.user;
223223- ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
223223+ ensureDBOwnership = true;
224224 }
225225 ];
226226 };
···529529 assertion = cfg.database.password != null -> cfg.database.passwordFile == null;
530530 message = "Cannot set both password and passwordFile";
531531 }
532532+ {
533533+ assertion = cfg.database.createLocally -> cfg.database.name == cfg.user && cfg.database.user == cfg.user;
534534+ message = ''
535535+ When creating a database via NixOS, the db user and db name must be equal!
536536+ If you already have an existing DB+user and this assertion is new, you can safely set
537537+ `services.tt-rss.database.createLocally` to `false` because removal of `ensureUsers`
538538+ and `ensureDatabases` doesn't have any effect.
539539+ '';
540540+ }
532541 ];
533542534543 services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") {
···632641 enable = mkDefault true;
633642 ensureDatabases = [ cfg.database.name ];
634643 ensureUsers = [
635635- { name = cfg.user;
636636- ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
644644+ { name = cfg.database.user;
645645+ ensureDBOwnership = true;
637646 }
638647 ];
639648 };