postgres-operator/docs/pgbouncer-generated-config.md

5.7 KiB

Operator-generated PgBouncer config (Helm)

By default the connection pooler relies on the PgBouncer image's entrypoint to render pgbouncer.ini from environment variables (this is what the bundled ghcr.io/zalando/postgres-operator/pgbouncer image does). Some images — for example the Chainguard FIPS PgBouncer image — ship no such entrypoint.

When connection_pooler_generate_config is enabled, the operator renders the config itself instead of relying on the image. For every pooler it:

  • renders pgbouncer.ini and stores it in a ConfigMap named <pooler>-config (e.g. acid-minimal-cluster-pooler-config);
  • mounts that ConfigMap into the pooler container at connection_pooler_config_path using a subPath;
  • overrides the container command/args (when set) so PgBouncer reads the mounted file;
  • stamps the pod template with an acid.zalan.do/pgbouncer-config-checksum annotation, so the pooler restarts automatically when the rendered config changes.

The feature is opt-in; with the default connection_pooler_generate_config: false nothing changes for existing clusters.

1. Configure the operator via the Helm chart

These settings are operator-wide defaults and live under configConnectionPooler in the chart's values.yaml.

configConnectionPooler:
  # Point the pooler at an image whose entrypoint does NOT render pgbouncer.ini.
  # Replace with your actual image reference.
  connection_pooler_image: "cgr.dev/chainguard/pgbouncer-fips:latest"

  # Let the operator render and own pgbouncer.ini.
  connection_pooler_generate_config: true

  # Optional: override the container entrypoint. Leave unset to keep the image's
  # own entrypoint. Set it when the image has no entrypoint that starts pgbouncer.
  connection_pooler_command:
    - "pgbouncer"
  # Args are applied only when generate_config is true. The default already points
  # pgbouncer at the mounted config file, so you usually don't need to change it.
  connection_pooler_args:
    - "/etc/pgbouncer/pgbouncer.ini"

  # Written into the generated pgbouncer.ini.
  connection_pooler_auth_type: "scram-sha-256"
  # Where the ConfigMap is mounted (and where args/command should point).
  connection_pooler_config_path: "/etc/pgbouncer/pgbouncer.ini"

Install or upgrade the operator with these values:

helm upgrade --install postgres-operator ./charts/postgres-operator \
  --namespace postgres-operator --create-namespace \
  -f values-pooler.yaml

Or set individual values inline:

helm upgrade --install postgres-operator ./charts/postgres-operator \
  --namespace postgres-operator --create-namespace \
  --set configConnectionPooler.connection_pooler_generate_config=true \
  --set configConnectionPooler.connection_pooler_image="cgr.dev/chainguard/pgbouncer-fips:latest"
Value (under configConnectionPooler) Default Purpose
connection_pooler_generate_config false Master switch — render pgbouncer.ini into an operator-owned ConfigMap.
connection_pooler_command (unset) Container command override; unset keeps the image entrypoint. Applied only when generating.
connection_pooler_args ["/etc/pgbouncer/pgbouncer.ini"] Container args; applied only when generating.
connection_pooler_auth_type scram-sha-256 auth_type written into the rendered config.
connection_pooler_config_path /etc/pgbouncer/pgbouncer.ini Mount path of the generated config.

2. Enable the pooler on a Postgres cluster

The operator settings above only take effect for clusters that actually run a pooler. Enable it in the postgresql manifest:

apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
  name: acid-minimal-cluster
  namespace: default
spec:
  teamId: "acid"
  postgresql:
    version: "17"
  numberOfInstances: 2
  volume:
    size: 1Gi

  # Run a master connection pooler for this cluster.
  enableConnectionPooler: true
  # Optionally also pool replica connections:
  # enableReplicaConnectionPooler: true

  # Per-cluster pooler overrides are optional; defaults come from the operator config.
  connectionPooler:
    numberOfInstances: 2
    mode: "transaction"

3. Verify

# The operator-owned config map for the master pooler:
kubectl get configmap acid-minimal-cluster-pooler-config -o yaml

# Inspect the rendered pgbouncer.ini:
kubectl get configmap acid-minimal-cluster-pooler-config \
  -o jsonpath='{.data.pgbouncer\.ini}'

# Confirm the pooler pod mounts it and carries the checksum annotation:
kubectl get pod -l connection-pooler=acid-minimal-cluster-pooler \
  -o jsonpath='{.items[0].metadata.annotations.acid\.zalan\.do/pgbouncer-config-checksum}'

A rendered config looks roughly like:

[databases]
* = host=acid-minimal-cluster port=5432

[pgbouncer]
pool_mode = transaction
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
auth_query = SELECT * FROM pooler.user_lookup($1)
server_tls_sslmode = require
default_pool_size = 15
max_db_connections = 30

When the cluster has TLS configured (spec.tls), the operator additionally renders client_tls_sslmode, client_tls_key_file, and client_tls_cert_file.

Notes

  • connection_pooler_command and connection_pooler_args are applied only when connection_pooler_generate_config is true. With generation off, the image entrypoint runs unchanged.
  • Changing any input that affects the rendered config (mode, auth type, sizes, TLS) updates the ConfigMap and changes the checksum annotation, which rolls the pooler pods automatically.
  • The same parameters are available on the OperatorConfiguration CRD as connection_pooler_generate_config, connection_pooler_command, connection_pooler_args, connection_pooler_auth_type, and connection_pooler_config_path.