grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / backup / postgresql-wal-receiver.nix
blobb355637ff5a0386c7260ddc0e9a3af582d94b9fa
1 { config, lib, pkgs, ... }:
2 let
3   receiverSubmodule = {
4     options = {
5       postgresqlPackage = lib.mkPackageOption pkgs "postgresql" {
6         example = "postgresql_15";
7       };
9       directory = lib.mkOption {
10         type = lib.types.path;
11         example = lib.literalExpression "/mnt/pg_wal/main/";
12         description = ''
13           Directory to write the output to.
14         '';
15       };
17       statusInterval = lib.mkOption {
18         type = lib.types.int;
19         default = 10;
20         description = ''
21           Specifies the number of seconds between status packets sent back to the server.
22           This allows for easier monitoring of the progress from server.
23           A value of zero disables the periodic status updates completely,
24           although an update will still be sent when requested by the server, to avoid timeout disconnect.
25         '';
26       };
28       slot = lib.mkOption {
29         type = lib.types.str;
30         default = "";
31         example = "some_slot_name";
32         description = ''
33           Require {command}`pg_receivewal` to use an existing replication slot (see
34           [Section 26.2.6 of the PostgreSQL manual](https://www.postgresql.org/docs/current/warm-standby.html#STREAMING-REPLICATION-SLOTS)).
35           When this option is used, {command}`pg_receivewal` will report a flush position to the server,
36           indicating when each segment has been synchronized to disk so that the server can remove that segment if it is not otherwise needed.
38           When the replication client of {command}`pg_receivewal` is configured on the server as a synchronous standby,
39           then using a replication slot will report the flush position to the server, but only when a WAL file is closed.
40           Therefore, that configuration will cause transactions on the primary to wait for a long time and effectively not work satisfactorily.
41           The option {option}`synchronous` must be specified in addition to make this work correctly.
42         '';
43       };
45       synchronous = lib.mkOption {
46         type = lib.types.bool;
47         default = false;
48         description = ''
49           Flush the WAL data to disk immediately after it has been received.
50           Also send a status packet back to the server immediately after flushing, regardless of {option}`statusInterval`.
52           This option should be specified if the replication client of {command}`pg_receivewal` is configured on the server as a synchronous standby,
53           to ensure that timely feedback is sent to the server.
54         '';
55       };
57       compress = lib.mkOption {
58         type = lib.types.ints.between 0 9;
59         default = 0;
60         description = ''
61           Enables gzip compression of write-ahead logs, and specifies the compression level
62           (`0` through `9`, `0` being no compression and `9` being best compression).
63           The suffix `.gz` will automatically be added to all filenames.
65           This option requires PostgreSQL >= 10.
66         '';
67       };
69       connection = lib.mkOption {
70         type = lib.types.str;
71         example = "postgresql://user@somehost";
72         description = ''
73           Specifies parameters used to connect to the server, as a connection string.
74           See [Section 34.1.1 of the PostgreSQL manual](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING) for more information.
76           Because {command}`pg_receivewal` doesn't connect to any particular database in the cluster,
77           database name in the connection string will be ignored.
78         '';
79       };
81       extraArgs = lib.mkOption {
82         type = with lib.types; listOf str;
83         default = [ ];
84         example = lib.literalExpression ''
85           [
86             "--no-sync"
87           ]
88         '';
89         description = ''
90           A list of extra arguments to pass to the {command}`pg_receivewal` command.
91         '';
92       };
94       environment = lib.mkOption {
95         type = with lib.types; attrsOf str;
96         default = { };
97         example = lib.literalExpression ''
98           {
99             PGPASSFILE = "/private/passfile";
100             PGSSLMODE = "require";
101           }
102         '';
103         description = ''
104           Environment variables passed to the service.
105           Usable parameters are listed in [Section 34.14 of the PostgreSQL manual](https://www.postgresql.org/docs/current/libpq-envars.html).
106         '';
107       };
108     };
109   };
111 in {
112   options = {
113     services.postgresqlWalReceiver = {
114       receivers = lib.mkOption {
115         type = with lib.types; attrsOf (submodule receiverSubmodule);
116         default = { };
117         example = lib.literalExpression ''
118           {
119             main = {
120               postgresqlPackage = pkgs.postgresql_15;
121               directory = /mnt/pg_wal/main/;
122               slot = "main_wal_receiver";
123               connection = "postgresql://user@somehost";
124             };
125           }
126         '';
127         description = ''
128           PostgreSQL WAL receivers.
129           Stream write-ahead logs from a PostgreSQL server using {command}`pg_receivewal` (formerly {command}`pg_receivexlog`).
130           See [the man page](https://www.postgresql.org/docs/current/app-pgreceivewal.html) for more information.
131         '';
132       };
133     };
134   };
136   config = let
137     receivers = config.services.postgresqlWalReceiver.receivers;
138   in lib.mkIf (receivers != { }) {
139     users = {
140       users.postgres = {
141         uid = config.ids.uids.postgres;
142         group = "postgres";
143         description = "PostgreSQL server user";
144       };
146       groups.postgres = {
147         gid = config.ids.gids.postgres;
148       };
149     };
151     assertions = lib.concatLists (lib.attrsets.mapAttrsToList (name: config: [
152       {
153         assertion = config.compress > 0 -> lib.versionAtLeast config.postgresqlPackage.version "10";
154         message = "Invalid configuration for WAL receiver \"${name}\": compress requires PostgreSQL version >= 10.";
155       }
156     ]) receivers);
158     systemd.tmpfiles.rules = lib.mapAttrsToList (name: config: ''
159       d ${lib.escapeShellArg config.directory} 0750 postgres postgres - -
160     '') receivers;
162     systemd.services = lib.mapAttrs' (name: config: lib.nameValuePair "postgresql-wal-receiver-${name}" {
163       description = "PostgreSQL WAL receiver (${name})";
164       wantedBy = [ "multi-user.target" ];
165       startLimitIntervalSec = 0; # retry forever, useful in case of network disruption
167       serviceConfig = {
168         User = "postgres";
169         Group = "postgres";
170         KillSignal = "SIGINT";
171         Restart = "always";
172         RestartSec = 60;
173       };
175       inherit (config) environment;
177       script = let
178         receiverCommand = postgresqlPackage:
179          if (lib.versionAtLeast postgresqlPackage.version "10")
180            then "${postgresqlPackage}/bin/pg_receivewal"
181            else "${postgresqlPackage}/bin/pg_receivexlog";
182       in ''
183         ${receiverCommand config.postgresqlPackage} \
184           --no-password \
185           --directory=${lib.escapeShellArg config.directory} \
186           --status-interval=${toString config.statusInterval} \
187           --dbname=${lib.escapeShellArg config.connection} \
188           ${lib.optionalString (config.compress > 0) "--compress=${toString config.compress}"} \
189           ${lib.optionalString (config.slot != "") "--slot=${lib.escapeShellArg config.slot}"} \
190           ${lib.optionalString config.synchronous "--synchronous"} \
191           ${lib.concatStringsSep " " config.extraArgs}
192       '';
193     }) receivers;
194   };
196   meta.maintainers = with lib.maintainers; [ pacien ];