1{
2 config,
3 lib,
4 utils,
5 pkgs,
6 ...
7}:
8let
9 cfg = config.services.pgbouncer;
10
11 settingsFormat = pkgs.formats.ini { };
12 configFile = settingsFormat.generate "pgbouncer.ini" (
13 lib.filterAttrsRecursive (_: v: v != null) cfg.settings
14 );
15 configPath = "pgbouncer/pgbouncer.ini";
16in
17{
18 imports = [
19 (lib.mkRemovedOptionModule [ "services" "pgbouncer" "logFile" ] ''
20 `services.pgbouncer.logFile` has been removed, use `services.pgbouncer.settings.pgbouncer.logfile`
21 instead.
22 Please note that the new option expects an absolute path
23 whereas the old option accepted paths relative to pgbouncer's home dir.
24 '')
25 (lib.mkRenamedOptionModule
26 [ "services" "pgbouncer" "listenAddress" ]
27 [ "services" "pgbouncer" "settings" "pgbouncer" "listen_addr" ]
28 )
29 (lib.mkRenamedOptionModule
30 [ "services" "pgbouncer" "listenPort" ]
31 [ "services" "pgbouncer" "settings" "pgbouncer" "listen_port" ]
32 )
33 (lib.mkRenamedOptionModule
34 [ "services" "pgbouncer" "poolMode" ]
35 [ "services" "pgbouncer" "settings" "pgbouncer" "pool_mode" ]
36 )
37 (lib.mkRenamedOptionModule
38 [ "services" "pgbouncer" "maxClientConn" ]
39 [ "services" "pgbouncer" "settings" "pgbouncer" "max_client_conn" ]
40 )
41 (lib.mkRenamedOptionModule
42 [ "services" "pgbouncer" "defaultPoolSize" ]
43 [ "services" "pgbouncer" "settings" "pgbouncer" "default_pool_size" ]
44 )
45 (lib.mkRenamedOptionModule
46 [ "services" "pgbouncer" "maxDbConnections" ]
47 [ "services" "pgbouncer" "settings" "pgbouncer" "max_db_connections" ]
48 )
49 (lib.mkRenamedOptionModule
50 [ "services" "pgbouncer" "maxUserConnections" ]
51 [ "services" "pgbouncer" "settings" "pgbouncer" "max_user_connections" ]
52 )
53 (lib.mkRenamedOptionModule
54 [ "services" "pgbouncer" "ignoreStartupParameters" ]
55 [ "services" "pgbouncer" "settings" "pgbouncer" "ignore_startup_parameters" ]
56 )
57 (lib.mkRenamedOptionModule
58 [ "services" "pgbouncer" "databases" ]
59 [ "services" "pgbouncer" "settings" "databases" ]
60 )
61 (lib.mkRenamedOptionModule
62 [ "services" "pgbouncer" "users" ]
63 [ "services" "pgbouncer" "settings" "users" ]
64 )
65 (lib.mkRenamedOptionModule
66 [ "services" "pgbouncer" "peers" ]
67 [ "services" "pgbouncer" "settings" "peers" ]
68 )
69 (lib.mkRenamedOptionModule
70 [ "services" "pgbouncer" "authType" ]
71 [ "services" "pgbouncer" "settings" "pgbouncer" "auth_type" ]
72 )
73 (lib.mkRenamedOptionModule
74 [ "services" "pgbouncer" "authHbaFile" ]
75 [ "services" "pgbouncer" "settings" "pgbouncer" "auth_hba_file" ]
76 )
77 (lib.mkRenamedOptionModule
78 [ "services" "pgbouncer" "authFile" ]
79 [ "services" "pgbouncer" "settings" "pgbouncer" "auth_file" ]
80 )
81 (lib.mkRenamedOptionModule
82 [ "services" "pgbouncer" "authUser" ]
83 [ "services" "pgbouncer" "settings" "pgbouncer" "auth_user" ]
84 )
85 (lib.mkRenamedOptionModule
86 [ "services" "pgbouncer" "authQuery" ]
87 [ "services" "pgbouncer" "settings" "pgbouncer" "auth_query" ]
88 )
89 (lib.mkRenamedOptionModule
90 [ "services" "pgbouncer" "authDbname" ]
91 [ "services" "pgbouncer" "settings" "pgbouncer" "auth_dbname" ]
92 )
93 (lib.mkRenamedOptionModule
94 [ "services" "pgbouncer" "adminUsers" ]
95 [ "services" "pgbouncer" "settings" "pgbouncer" "admin_users" ]
96 )
97 (lib.mkRenamedOptionModule
98 [ "services" "pgbouncer" "statsUsers" ]
99 [ "services" "pgbouncer" "settings" "pgbouncer" "stats_users" ]
100 )
101 (lib.mkRenamedOptionModule
102 [ "services" "pgbouncer" "verbose" ]
103 [ "services" "pgbouncer" "settings" "pgbouncer" "verbose" ]
104 )
105 (lib.mkChangedOptionModule
106 [ "services" "pgbouncer" "syslog" "enable" ]
107 [ "services" "pgbouncer" "settings" "pgbouncer" "syslog" ]
108 (
109 config:
110 let
111 enable = lib.getAttrFromPath [ "services" "pgbouncer" "syslog" "enable" ] config;
112 in
113 if enable then 1 else 0
114 )
115 )
116 (lib.mkRenamedOptionModule
117 [ "services" "pgbouncer" "syslog" "syslogIdent" ]
118 [ "services" "pgbouncer" "settings" "pgbouncer" "syslog_ident" ]
119 )
120 (lib.mkRenamedOptionModule
121 [ "services" "pgbouncer" "syslog" "syslogFacility" ]
122 [ "services" "pgbouncer" "settings" "pgbouncer" "syslog_facility" ]
123 )
124 (lib.mkRenamedOptionModule
125 [ "services" "pgbouncer" "tls" "client" "sslmode" ]
126 [ "services" "pgbouncer" "settings" "pgbouncer" "client_tls_sslmode" ]
127 )
128 (lib.mkRenamedOptionModule
129 [ "services" "pgbouncer" "tls" "client" "keyFile" ]
130 [ "services" "pgbouncer" "settings" "pgbouncer" "client_tls_key_file" ]
131 )
132 (lib.mkRenamedOptionModule
133 [ "services" "pgbouncer" "tls" "client" "certFile" ]
134 [ "services" "pgbouncer" "settings" "pgbouncer" "client_tls_cert_file" ]
135 )
136 (lib.mkRenamedOptionModule
137 [ "services" "pgbouncer" "tls" "client" "caFile" ]
138 [ "services" "pgbouncer" "settings" "pgbouncer" "client_tls_ca_file" ]
139 )
140 (lib.mkRenamedOptionModule
141 [ "services" "pgbouncer" "tls" "server" "sslmode" ]
142 [ "services" "pgbouncer" "settings" "pgbouncer" "server_tls_sslmode" ]
143 )
144 (lib.mkRenamedOptionModule
145 [ "services" "pgbouncer" "tls" "server" "keyFile" ]
146 [ "services" "pgbouncer" "settings" "pgbouncer" "server_tls_key_file" ]
147 )
148 (lib.mkRenamedOptionModule
149 [ "services" "pgbouncer" "tls" "server" "certFile" ]
150 [ "services" "pgbouncer" "settings" "pgbouncer" "server_tls_cert_file" ]
151 )
152 (lib.mkRenamedOptionModule
153 [ "services" "pgbouncer" "tls" "server" "caFile" ]
154 [ "services" "pgbouncer" "settings" "pgbouncer" "server_tls_ca_file" ]
155 )
156 (lib.mkRemovedOptionModule [
157 "services"
158 "pgbouncer"
159 "extraConfig"
160 ] "Use services.pgbouncer.settings instead.")
161 ];
162
163 options.services.pgbouncer = {
164 enable = lib.mkEnableOption "PostgreSQL connection pooler";
165
166 package = lib.mkPackageOption pkgs "pgbouncer" { };
167
168 openFirewall = lib.mkOption {
169 type = lib.types.bool;
170 default = false;
171 description = ''
172 Whether to automatically open the specified TCP port in the firewall.
173 '';
174 };
175
176 settings = lib.mkOption {
177 type = lib.types.submodule {
178 freeformType = settingsFormat.type;
179 options = {
180 pgbouncer = {
181 listen_port = lib.mkOption {
182 type = lib.types.port;
183 default = 6432;
184 description = ''
185 Which port to listen on. Applies to both TCP and Unix sockets.
186 '';
187 };
188
189 listen_addr = lib.mkOption {
190 type = lib.types.nullOr lib.types.commas;
191 example = "*";
192 default = null;
193 description = ''
194 Specifies a list (comma-separated) of addresses where to listen for TCP connections.
195 You may also use * meaning “listen on all addresses”.
196 When not set, only Unix socket connections are accepted.
197
198 Addresses can be specified numerically (IPv4/IPv6) or by name.
199 '';
200 };
201
202 pool_mode = lib.mkOption {
203 type = lib.types.enum [
204 "session"
205 "transaction"
206 "statement"
207 ];
208 default = "session";
209 description = ''
210 Specifies when a server connection can be reused by other clients.
211
212 session
213 Server is released back to pool after client disconnects. Default.
214 transaction
215 Server is released back to pool after transaction finishes.
216 statement
217 Server is released back to pool after query finishes.
218 Transactions spanning multiple statements are disallowed in this mode.
219 '';
220 };
221
222 max_client_conn = lib.mkOption {
223 type = lib.types.int;
224 default = 100;
225 description = ''
226 Maximum number of client connections allowed.
227
228 When this setting is increased, then the file descriptor limits in the operating system
229 might also have to be increased. Note that the number of file descriptors potentially
230 used is more than maxClientConn. If each user connects under its own user name to the server,
231 the theoretical maximum used is:
232 maxClientConn + (max pool_size * total databases * total users)
233
234 If a database user is specified in the connection string (all users connect under the same user name),
235 the theoretical maximum is:
236 maxClientConn + (max pool_size * total databases)
237
238 The theoretical maximum should never be reached, unless somebody deliberately crafts a special load for it.
239 Still, it means you should set the number of file descriptors to a safely high number.
240 '';
241 };
242
243 default_pool_size = lib.mkOption {
244 type = lib.types.int;
245 default = 20;
246 description = ''
247 How many server connections to allow per user/database pair.
248 Can be overridden in the per-database configuration.
249 '';
250 };
251
252 max_db_connections = lib.mkOption {
253 type = lib.types.int;
254 default = 0;
255 description = ''
256 Do not allow more than this many server connections per database (regardless of user).
257 This considers the PgBouncer database that the client has connected to,
258 not the PostgreSQL database of the outgoing connection.
259
260 This can also be set per database in the [databases] section.
261
262 Note that when you hit the limit, closing a client connection to one pool will
263 not immediately allow a server connection to be established for another pool,
264 because the server connection for the first pool is still open.
265 Once the server connection closes (due to idle timeout),
266 a new server connection will immediately be opened for the waiting pool.
267
268 0 = unlimited
269 '';
270 };
271
272 max_user_connections = lib.mkOption {
273 type = lib.types.int;
274 default = 0;
275 description = ''
276 Do not allow more than this many server connections per user (regardless of database).
277 This considers the PgBouncer user that is associated with a pool,
278 which is either the user specified for the server connection
279 or in absence of that the user the client has connected as.
280
281 This can also be set per user in the [users] section.
282
283 Note that when you hit the limit, closing a client connection to one pool
284 will not immediately allow a server connection to be established for another pool,
285 because the server connection for the first pool is still open.
286 Once the server connection closes (due to idle timeout), a new server connection
287 will immediately be opened for the waiting pool.
288
289 0 = unlimited
290 '';
291 };
292
293 ignore_startup_parameters = lib.mkOption {
294 type = lib.types.nullOr lib.types.commas;
295 example = "extra_float_digits";
296 default = null;
297 description = ''
298 By default, PgBouncer allows only parameters it can keep track of in startup packets:
299 client_encoding, datestyle, timezone and standard_conforming_strings.
300
301 All others parameters will raise an error.
302 To allow others parameters, they can be specified here, so that PgBouncer knows that
303 they are handled by the admin and it can ignore them.
304
305 If you need to specify multiple values, use a comma-separated list.
306
307 IMPORTANT: When using prometheus-pgbouncer-exporter, you need:
308 extra_float_digits
309 <https://github.com/prometheus-community/pgbouncer_exporter#pgbouncer-configuration>
310 '';
311 };
312 };
313 databases = lib.mkOption {
314 type = lib.types.attrsOf lib.types.str;
315 default = { };
316 example = {
317 exampledb = "host=/run/postgresql/ port=5432 auth_user=exampleuser dbname=exampledb sslmode=require";
318 bardb = "host=localhost dbname=bazdb";
319 foodb = "host=host1.example.com port=5432";
320 };
321 description = ''
322 Detailed information about PostgreSQL database definitions:
323 <https://www.pgbouncer.org/config.html#section-databases>
324 '';
325 };
326 users = lib.mkOption {
327 type = lib.types.attrsOf lib.types.str;
328 default = { };
329 example = {
330 user1 = "pool_mode=session";
331 };
332 description = ''
333 Optional.
334
335 Detailed information about PostgreSQL user definitions:
336 <https://www.pgbouncer.org/config.html#section-users>
337 '';
338 };
339
340 peers = lib.mkOption {
341 type = lib.types.attrsOf lib.types.str;
342 default = { };
343 example = {
344 "1" = "host=host1.example.com";
345 "2" = "host=/tmp/pgbouncer-2 port=5555";
346 };
347 description = ''
348 Optional.
349
350 Detailed information about PostgreSQL database definitions:
351 <https://www.pgbouncer.org/config.html#section-peers>
352 '';
353 };
354 };
355 };
356 default = { };
357 description = ''
358 Configuration for PgBouncer, see <https://www.pgbouncer.org/config.html>
359 for supported values.
360 '';
361 };
362
363 # Linux settings
364 openFilesLimit = lib.mkOption {
365 type = lib.types.int;
366 default = 65536;
367 description = ''
368 Maximum number of open files.
369 '';
370 };
371
372 user = lib.mkOption {
373 type = lib.types.str;
374 default = "pgbouncer";
375 description = ''
376 The user pgbouncer is run as.
377 '';
378 };
379
380 group = lib.mkOption {
381 type = lib.types.str;
382 default = "pgbouncer";
383 description = ''
384 The group pgbouncer is run as.
385 '';
386 };
387
388 homeDir = lib.mkOption {
389 type = lib.types.path;
390 default = "/var/lib/pgbouncer";
391 description = ''
392 Specifies the home directory.
393 '';
394 };
395 };
396
397 config = lib.mkIf cfg.enable {
398 users.groups.${cfg.group} = { };
399 users.users.${cfg.user} = {
400 description = "PgBouncer service user";
401 group = cfg.group;
402 home = cfg.homeDir;
403 createHome = true;
404 isSystemUser = true;
405 };
406
407 environment.etc.${configPath}.source = configFile;
408
409 # Default to RuntimeDirectory instead of /tmp.
410 services.pgbouncer.settings.pgbouncer.unix_socket_dir = lib.mkDefault "/run/pgbouncer";
411
412 systemd.services.pgbouncer = {
413 description = "PgBouncer - PostgreSQL connection pooler";
414 wants = [ "network-online.target" ];
415 after = [ "network-online.target" ];
416 wantedBy = [ "multi-user.target" ];
417 reloadTriggers = [ configFile ];
418 serviceConfig = {
419 Type = "notify-reload";
420 User = cfg.user;
421 Group = cfg.group;
422 ExecStart = utils.escapeSystemdExecArgs [
423 (lib.getExe pkgs.pgbouncer)
424 "/etc/${configPath}"
425 ];
426 RuntimeDirectory = "pgbouncer";
427 LimitNOFILE = cfg.openFilesLimit;
428 };
429 };
430
431 networking.firewall = lib.mkIf cfg.openFirewall {
432 allowedTCPPorts = [
433 (cfg.settings.pgbouncer.listen_port or 6432)
434 ];
435 };
436 };
437
438 meta.maintainers = [ lib.maintainers._1000101 ];
439}