lol
1# PostgreSQL {#module-postgresql}
2
3<!-- FIXME: render nicely -->
4<!-- FIXME: source can be added automatically -->
5
6*Source:* {file}`modules/services/databases/postgresql.nix`
7
8*Upstream documentation:* <https://www.postgresql.org/docs/>
9
10<!-- FIXME: more stuff, like maintainer? -->
11
12PostgreSQL is an advanced, free relational database.
13<!-- MORE -->
14
15## Configuring {#module-services-postgres-configuring}
16
17To enable PostgreSQL, add the following to your {file}`configuration.nix`:
18```
19services.postgresql.enable = true;
20services.postgresql.package = pkgs.postgresql_15;
21```
22Note that you are required to specify the desired version of PostgreSQL (e.g. `pkgs.postgresql_15`). Since upgrading your PostgreSQL version requires a database dump and reload (see below), NixOS cannot provide a default value for [](#opt-services.postgresql.package) such as the most recent release of PostgreSQL.
23
24<!--
25After running {command}`nixos-rebuild`, you can verify
26whether PostgreSQL works by running {command}`psql`:
27
28```ShellSession
29$ psql
30psql (9.2.9)
31Type "help" for help.
32
33alice=>
34```
35-->
36
37By default, PostgreSQL stores its databases in {file}`/var/lib/postgresql/$psqlSchema`. You can override this using [](#opt-services.postgresql.dataDir), e.g.
38```
39services.postgresql.dataDir = "/data/postgresql";
40```
41
42## Initializing {#module-services-postgres-initializing}
43
44As of NixOS 23.11,
45`services.postgresql.ensureUsers.*.ensurePermissions` has been
46deprecated, after a change to default permissions in PostgreSQL 15
47invalidated most of its previous use cases:
48
49- In psql < 15, `ALL PRIVILEGES` used to include `CREATE TABLE`, where
50 in psql >= 15 that would be a separate permission
51- psql >= 15 instead gives only the database owner create permissions
52- Even on psql < 15 (or databases migrated to >= 15), it is
53 recommended to manually assign permissions along these lines
54 - https://www.postgresql.org/docs/release/15.0/
55 - https://www.postgresql.org/docs/15/ddl-schemas.html#DDL-SCHEMAS-PRIV
56
57### Assigning ownership {#module-services-postgres-initializing-ownership}
58
59Usually, the database owner should be a database user of the same
60name. This can be done with
61`services.postgresql.ensureUsers.*.ensureDBOwnership = true;`.
62
63If the database user name equals the connecting system user name,
64postgres by default will accept a passwordless connection via unix
65domain socket. This makes it possible to run many postgres-backed
66services without creating any database secrets at all
67
68### Assigning extra permissions {#module-services-postgres-initializing-extra-permissions}
69
70For many cases, it will be enough to have the database user be the
71owner. Until `services.postgresql.ensureUsers.*.ensurePermissions` has
72been re-thought, if more users need access to the database, please use
73one of the following approaches:
74
75**WARNING:** `services.postgresql.initialScript` is not recommended
76for `ensurePermissions` replacement, as that is *only run on first
77start of PostgreSQL*.
78
79**NOTE:** all of these methods may be obsoleted, when `ensure*` is
80reworked, but it is expected that they will stay viable for running
81database migrations.
82
83**NOTE:** please make sure that any added migrations are idempotent (re-runnable).
84
85#### as superuser {#module-services-postgres-initializing-extra-permissions-superuser}
86
87**Advantage:** compatible with postgres < 15, because it's run
88as the database superuser `postgres`.
89
90##### in database `postStart` {#module-services-postgres-initializing-extra-permissions-superuser-post-start}
91
92**Disadvantage:** need to take care of ordering yourself. In this
93example, `mkAfter` ensures that permissions are assigned after any
94databases from `ensureDatabases` and `extraUser1` from `ensureUsers`
95are already created.
96
97```nix
98 systemd.services.postgresql.postStart = lib.mkAfter ''
99 $PSQL service1 -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "extraUser1"'
100 $PSQL service1 -c 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO "extraUser1"'
101 # ....
102 '';
103```
104
105##### in intermediate oneshot service {#module-services-postgres-initializing-extra-permissions-superuser-oneshot}
106
107```nix
108 systemd.services."migrate-service1-db1" = {
109 serviceConfig.Type = "oneshot";
110 requiredBy = "service1.service";
111 before = "service1.service";
112 after = "postgresql.service";
113 serviceConfig.User = "postgres";
114 environment.PSQL = "psql --port=${toString services.postgresql.port}";
115 path = [ postgresql ];
116 script = ''
117 $PSQL service1 -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "extraUser1"'
118 $PSQL service1 -c 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO "extraUser1"'
119 # ....
120 '';
121 };
122```
123
124#### as service user {#module-services-postgres-initializing-extra-permissions-service-user}
125
126**Advantage:** re-uses systemd's dependency ordering;
127
128**Disadvantage:** relies on service user having grant permission. To be combined with `ensureDBOwnership`.
129
130##### in service `preStart` {#module-services-postgres-initializing-extra-permissions-service-user-pre-start}
131
132```nix
133 environment.PSQL = "psql --port=${toString services.postgresql.port}";
134 path = [ postgresql ];
135 systemd.services."service1".preStart = ''
136 $PSQL -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "extraUser1"'
137 $PSQL -c 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO "extraUser1"'
138 # ....
139 '';
140```
141
142##### in intermediate oneshot service {#module-services-postgres-initializing-extra-permissions-service-user-oneshot}
143
144```nix
145 systemd.services."migrate-service1-db1" = {
146 serviceConfig.Type = "oneshot";
147 requiredBy = "service1.service";
148 before = "service1.service";
149 after = "postgresql.service";
150 serviceConfig.User = "service1";
151 environment.PSQL = "psql --port=${toString services.postgresql.port}";
152 path = [ postgresql ];
153 script = ''
154 $PSQL -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "extraUser1"'
155 $PSQL -c 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO "extraUser1"'
156 # ....
157 '';
158 };
159```
160
161## Upgrading {#module-services-postgres-upgrading}
162
163::: {.note}
164The steps below demonstrate how to upgrade from an older version to `pkgs.postgresql_13`.
165These instructions are also applicable to other versions.
166:::
167
168Major PostgreSQL upgrades require a downtime and a few imperative steps to be called. This is the case because
169each major version has some internal changes in the databases' state during major releases. Because of that,
170NixOS places the state into {file}`/var/lib/postgresql/<version>` where each `version`
171can be obtained like this:
172```
173$ nix-instantiate --eval -A postgresql_13.psqlSchema
174"13"
175```
176For an upgrade, a script like this can be used to simplify the process:
177```
178{ config, pkgs, ... }:
179{
180 environment.systemPackages = [
181 (let
182 # XXX specify the postgresql package you'd like to upgrade to.
183 # Do not forget to list the extensions you need.
184 newPostgres = pkgs.postgresql_13.withPackages (pp: [
185 # pp.plv8
186 ]);
187 in pkgs.writeScriptBin "upgrade-pg-cluster" ''
188 set -eux
189 # XXX it's perhaps advisable to stop all services that depend on postgresql
190 systemctl stop postgresql
191
192 export NEWDATA="/var/lib/postgresql/${newPostgres.psqlSchema}"
193
194 export NEWBIN="${newPostgres}/bin"
195
196 export OLDDATA="${config.services.postgresql.dataDir}"
197 export OLDBIN="${config.services.postgresql.package}/bin"
198
199 install -d -m 0700 -o postgres -g postgres "$NEWDATA"
200 cd "$NEWDATA"
201 sudo -u postgres $NEWBIN/initdb -D "$NEWDATA"
202
203 sudo -u postgres $NEWBIN/pg_upgrade \
204 --old-datadir "$OLDDATA" --new-datadir "$NEWDATA" \
205 --old-bindir $OLDBIN --new-bindir $NEWBIN \
206 "$@"
207 '')
208 ];
209}
210```
211
212The upgrade process is:
213
214 1. Rebuild nixos configuration with the configuration above added to your {file}`configuration.nix`. Alternatively, add that into separate file and reference it in `imports` list.
215 2. Login as root (`sudo su -`)
216 3. Run `upgrade-pg-cluster`. It will stop old postgresql, initialize a new one and migrate the old one to the new one. You may supply arguments like `--jobs 4` and `--link` to speedup migration process. See <https://www.postgresql.org/docs/current/pgupgrade.html> for details.
217 4. Change postgresql package in NixOS configuration to the one you were upgrading to via [](#opt-services.postgresql.package). Rebuild NixOS. This should start new postgres using upgraded data directory and all services you stopped during the upgrade.
218 5. After the upgrade it's advisable to analyze the new cluster.
219
220 - For PostgreSQL ≥ 14, use the `vacuumdb` command printed by the upgrades script.
221 - For PostgreSQL < 14, run (as `su -l postgres` in the [](#opt-services.postgresql.dataDir), in this example {file}`/var/lib/postgresql/13`):
222
223 ```
224 $ ./analyze_new_cluster.sh
225 ```
226
227 ::: {.warning}
228 The next step removes the old state-directory!
229 :::
230
231 ```
232 $ ./delete_old_cluster.sh
233 ```
234
235## Options {#module-services-postgres-options}
236
237A complete list of options for the PostgreSQL module may be found [here](#opt-services.postgresql.enable).
238
239## Plugins {#module-services-postgres-plugins}
240
241Plugins collection for each PostgreSQL version can be accessed with `.pkgs`. For example, for `pkgs.postgresql_15` package, its plugin collection is accessed by `pkgs.postgresql_15.pkgs`:
242```ShellSession
243$ nix repl '<nixpkgs>'
244
245Loading '<nixpkgs>'...
246Added 10574 variables.
247
248nix-repl> postgresql_15.pkgs.<TAB><TAB>
249postgresql_15.pkgs.cstore_fdw postgresql_15.pkgs.pg_repack
250postgresql_15.pkgs.pg_auto_failover postgresql_15.pkgs.pg_safeupdate
251postgresql_15.pkgs.pg_bigm postgresql_15.pkgs.pg_similarity
252postgresql_15.pkgs.pg_cron postgresql_15.pkgs.pg_topn
253postgresql_15.pkgs.pg_hll postgresql_15.pkgs.pgjwt
254postgresql_15.pkgs.pg_partman postgresql_15.pkgs.pgroonga
255...
256```
257
258To add plugins via NixOS configuration, set `services.postgresql.extraPlugins`:
259```
260services.postgresql.package = pkgs.postgresql_12;
261services.postgresql.extraPlugins = with pkgs.postgresql_12.pkgs; [
262 pg_repack
263 postgis
264];
265```
266
267You can build custom PostgreSQL-with-plugins (to be used outside of NixOS) using function `.withPackages`. For example, creating a custom PostgreSQL package in an overlay can look like:
268```
269self: super: {
270 postgresql_custom = self.postgresql_12.withPackages (ps: [
271 ps.pg_repack
272 ps.postgis
273 ]);
274}
275```
276
277Here's a recipe on how to override a particular plugin through an overlay:
278```
279self: super: {
280 postgresql_15 = super.postgresql_15.override { this = self.postgresql_15; } // {
281 pkgs = super.postgresql_15.pkgs // {
282 pg_repack = super.postgresql_15.pkgs.pg_repack.overrideAttrs (_: {
283 name = "pg_repack-v20181024";
284 src = self.fetchzip {
285 url = "https://github.com/reorg/pg_repack/archive/923fa2f3c709a506e111cc963034bf2fd127aa00.tar.gz";
286 sha256 = "17k6hq9xaax87yz79j773qyigm4fwk8z4zh5cyp6z0sxnwfqxxw5";
287 };
288 });
289 };
290 };
291}
292```
293
294## JIT (Just-In-Time compilation) {#module-services-postgres-jit}
295
296[JIT](https://www.postgresql.org/docs/current/jit-reason.html)-support in the PostgreSQL package
297is disabled by default because of the ~300MiB closure-size increase from the LLVM dependency. It
298can be optionally enabled in PostgreSQL with the following config option:
299
300```nix
301{
302 services.postgresql.enableJIT = true;
303}
304```
305
306This makes sure that the [`jit`](https://www.postgresql.org/docs/current/runtime-config-query.html#GUC-JIT)-setting
307is set to `on` and a PostgreSQL package with JIT enabled is used. Further tweaking of the JIT compiler, e.g. setting a different
308query cost threshold via [`jit_above_cost`](https://www.postgresql.org/docs/current/runtime-config-query.html#GUC-JIT-ABOVE-COST)
309can be done manually via [`services.postgresql.settings`](#opt-services.postgresql.settings).
310
311The attribute-names of JIT-enabled PostgreSQL packages are suffixed with `_jit`, i.e. for each `pkgs.postgresql`
312(and `pkgs.postgresql_<major>`) in `nixpkgs` there's also a `pkgs.postgresql_jit` (and `pkgs.postgresql_<major>_jit`).
313Alternatively, a JIT-enabled variant can be derived from a given `postgresql` package via `postgresql.withJIT`.
314This is also useful if it's not clear which attribute from `nixpkgs` was originally used (e.g. when working with
315[`config.services.postgresql.package`](#opt-services.postgresql.package) or if the package was modified via an
316overlay) since all modifications are propagated to `withJIT`. I.e.
317
318```nix
319with import <nixpkgs> {
320 overlays = [
321 (self: super: {
322 postgresql = super.postgresql.overrideAttrs (_: { pname = "foobar"; });
323 })
324 ];
325};
326postgresql.withJIT.pname
327```
328
329evaluates to `"foobar"`.