Skip to content

feat(pooler): operator-generated PgBouncer config - #3104

Open
mkm29 wants to merge 21 commits into
zalando:masterfrom
mkm29:feat/enhanced-pooler-config
Open

mkm29 wants to merge 21 commits into
zalando:masterfrom
mkm29:feat/enhanced-pooler-config

Conversation

@mkm29

@mkm29 mkm29 commented Jun 7, 2026

Copy link
Copy Markdown

feat(pooler): operator-generated PgBouncer config

Summary

Adds an opt-in mode where the operator renders pgbouncer.ini itself and manages it as an operator-owned ConfigMap, instead of relying on the pooler image to generate its own config from environment variables.

Motivation

The default Spilo/PgBouncer image renders pgbouncer.ini at startup, but some images (e.g. the Chainguard PgBouncer distroless image) ship no such entrypoint. Supporting them previously required a static ConfigMap plus a mutating webhook. This PR moves that responsibility into the operator and removes the workaround.

What changes

When connection_pooler_generate_config: true, for each pooler role the operator:

  • renders pgbouncer.ini (pool mode, auth type/query, TLS, sizes) and writes it to a <pooler>-config ConfigMap (create/sync/delete in the pooler lifecycle);
  • mounts it at config_path via subPath, and overrides the container command/args to point PgBouncer at it;
  • stamps a config checksum annotation on the pod template so config changes trigger a rollout.

Default is falseno behavior change for existing clusters.

New configuration (ConfigMap + OperatorConfiguration CRD)

Parameter Default Notes
connection_pooler_generate_config false master switch
connection_pooler_command (unset) keeps image entrypoint when empty
connection_pooler_args ["/etc/pgbouncer/pgbouncer.ini"] applied only when generating
connection_pooler_auth_type scram-sha-256 written into pgbouncer.ini
connection_pooler_config_path /etc/pgbouncer/pgbouncer.ini mount path

Tests

New/extended coverage in config_test.go, connection_pooler_test.go, and pgbouncer_config_test.go (ini rendering incl. TLS, checksum stability, ConfigMap generation, pod-template on/off, and sync create + update-on-drift). Verify by running the tests:

go generate ./...
go test ./pkg/util/config/ -run TestConnectionPoolerGenerateConfigDefaults -v
go test ./pkg/cluster/ -run 'TestConnectionPoolerSizes|TestPoolerEnv|ConnectionPooler|TestGeneratePgBouncerIni' -v

Also updated

Helm values.yaml, operator_parameters.md, both OperatorConfiguration CRDs, and the default operator-configuration manifest.

@FxKu FxKu added this to the wishlist milestone Jun 29, 2026
@cross-du

Copy link
Copy Markdown

Any chance of this PR and #3105 getting a review soon? It would be a big help for my use case.

@FxKu

FxKu commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

The PR is too big for the next release which I plan to drop this week, but I like to review and include it after that.

connection_pooler_config_path:
type: string
default: "/etc/pgbouncer/pgbouncer.ini"
patroni:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this diff does not look correct. You only need to add new fields to the pkg.api. package and the CRDs will be generated when running make for example

@@ -0,0 +1,133 @@
<h1>Operator-generated PgBouncer config (Helm)</h1>

@FxKu FxKu Sep 16, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't specific to helm chart, right? Not sure about a new single docs file. Can you move this section to the administrator.md (global config) and add one for users.md, too (local config)

@@ -0,0 +1,986 @@
---

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls remove this file from the PR

@@ -0,0 +1,84 @@
---

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls remove this file from the PR

connection_pooler_config_path:
type: string
default: "/etc/pgbouncer/pgbouncer.ini"
patroni:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, looks like the new fields got inserted incorrectly

not:
required:
- s3_wal_path
- gs_wal_path

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not remove fields in postgresql CRD

@@ -0,0 +1,4824 @@
---

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls remove this file from the PR

// +kubebuilder:default="scram-sha-256"
AuthType string `json:"connection_pooler_auth_type,omitempty"`
// +kubebuilder:default="/etc/pgbouncer/pgbouncer.ini"
ConfigPath string `json:"connection_pooler_config_path,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, it makes sense to also add these fields to the postgresql_type, too. At least we would need this for some teams to still use pgbouncer with md5 while globally we want scram to be the default

})
volumeMounts = append(volumeMounts, v1.VolumeMount{
Name: configVolumeName,
MountPath: c.OpConfig.ConnectionPooler.ConfigPath,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not 100% sure that this variable is always set at this point

if len(c.OpConfig.ConnectionPooler.Command) > 0 {
poolerContainer.Command = c.OpConfig.ConnectionPooler.Command
}
poolerContainer.Args = c.OpConfig.ConnectionPooler.Args

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here. Can Args be empty? Should we always set it or use an if like with Command

checksum, err := c.connectionPoolerConfigChecksum(role)
if err != nil {
return nil, err
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when we maybe just raise a warning when the checksum annotation is missing? I would not break the cluster creation just because an annotation of a pooler could not get generated

Comment thread mkdocs.yml
- Postgres Operator UI: 'operator-ui.md'
- Admin guide: 'administrator.md'
- User guide: 'user.md'
- PgBouncer generated config: 'pgbouncer-generated-config.md'

@FxKu FxKu Sep 16, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like I wrote above I would not create a dedicated docs file for this config option now but rather include it in admin and user docs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants