{ config, lib, pkgs, ... }: let txPort = config.m3ta.ports.get "pgbouncer-tx"; sessionPort = config.m3ta.ports.get "pgbouncer-session"; pgPort = config.m3ta.ports.get "postgres"; # Podman-Bridge-IP des Hosts hostBridgeIP = "10.89.0.1"; # pgbouncer (1.24+ als auch 1.25.2) lädt bei JEDEM TLS-Setup eine CA — auch # bei sslmode=prefer/require ohne Verifikation. Ohne explizite Datei fällt # es auf die OpenSSL-Default-Verify-Paths zurück, die hier fehlschlagen: # "TLS setup failed: failed to load CA: (null)" → Startabbruch. # Explizites Bundle umgeht das (verifiziert 2026-08-20 gegen 1.24.1 + 1.25.2). caBundle = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; commonSettings = { listen_addr = "127.0.0.1,${hostBridgeIP}"; auth_type = "scram-sha-256"; auth_file = config.age.secrets.pgbouncer-userlist.path; admin_users = "sascha_koenig"; stats_users = "sascha_koenig"; log_connections = 1; log_disconnections = 1; client_tls_sslmode = "prefer"; client_tls_cert_file = config.age.secrets.pgbouncer-tls-cert.path; client_tls_key_file = config.age.secrets.pgbouncer-tls-key.path; client_tls_protocols = "secure"; client_tls_ca_file = caBundle; server_reset_query = "DISCARD ALL"; server_check_query = "SELECT 1"; server_check_delay = 30; server_tls_sslmode = "prefer"; server_tls_protocols = "secure"; server_tls_ca_file = caBundle; }; mkDatabases = dbs: lib.listToAttrs (map (db: lib.nameValuePair db "host=127.0.0.1 port=${toString pgPort} dbname=${db}") dbs); in { services.pgbouncer = { enable = true; settings = { pgbouncer = commonSettings // { listen_port = txPort; pool_mode = "transaction"; max_client_conn = 1000; default_pool_size = 10; min_pool_size = 2; reserve_pool_size = 3; reserve_pool_timeout = 3; max_prepared_statements = 200; }; databases = mkDatabases [ "baserow" "litellm" "librechat_rag" "librechat_rag_dev" "metabase" "az_kpi_raw" ]; }; }; systemd.services.pgbouncer = { after = ["postgresql.service"]; requires = ["postgresql.service"]; }; environment.etc."pgbouncer/pgbouncer-session.ini".text = let dbLines = lib.concatStringsSep "\n" (lib.mapAttrsToList (name: conn: "${name} = ${conn}") (mkDatabases [ "outline" "zammad" "zammad_hr" "vaultwarden" "dash" ])); in '' [databases] ${dbLines} [pgbouncer] listen_addr = ${commonSettings.listen_addr} listen_port = ${toString sessionPort} pool_mode = session auth_type = ${commonSettings.auth_type} auth_file = ${config.age.secrets.pgbouncer-userlist.path} admin_users = ${commonSettings.admin_users} stats_users = ${commonSettings.stats_users} client_tls_sslmode = ${commonSettings.client_tls_sslmode} client_tls_cert_file = ${commonSettings.client_tls_cert_file} client_tls_key_file = ${commonSettings.client_tls_key_file} client_tls_protocols = ${commonSettings.client_tls_protocols} client_tls_ca_file = ${commonSettings.client_tls_ca_file} max_client_conn = 300 default_pool_size = 30 min_pool_size = 1 query_wait_timeout = 30 log_connections = 1 log_disconnections = 1 server_reset_query = DISCARD ALL server_check_query = SELECT 1 server_check_delay = 30 server_tls_sslmode = ${commonSettings.server_tls_sslmode} server_tls_protocols = ${commonSettings.server_tls_protocols} server_tls_ca_file = ${commonSettings.server_tls_ca_file} # PID/Socket getrennt von der Haupt-Instanz halten. pidfile = /run/pgbouncer-session/pgbouncer.pid unix_socket_dir = /run/pgbouncer-session ''; systemd.services.pgbouncer-session = { description = "PgBouncer (session pool) for prepared-statement apps"; after = ["postgresql.service"]; requires = ["postgresql.service"]; wantedBy = ["multi-user.target"]; serviceConfig = { User = "pgbouncer"; Group = "pgbouncer"; RuntimeDirectory = "pgbouncer-session"; ExecStart = "${pkgs.pgbouncer}/bin/pgbouncer /etc/pgbouncer/pgbouncer-session.ini"; Restart = "on-failure"; RestartSec = 5; }; }; networking.firewall.extraCommands = '' iptables -A INPUT -p tcp -s 127.0.0.1 --dport ${toString txPort} -j ACCEPT iptables -A INPUT -p tcp -s 10.89.0.0/24 --dport ${toString txPort} -j ACCEPT iptables -A INPUT -p tcp -s 127.0.0.1 --dport ${toString sessionPort} -j ACCEPT iptables -A INPUT -p tcp -s 10.89.0.0/24 --dport ${toString sessionPort} -j ACCEPT ''; }