EDB CloudNativePG Cluster 1.30.0-rc1 release notes v1.30.0-rc1

Released: 19 June 2026

This release of EDB Postgres® AI for CloudNativePG™ Cluster includes the following:

Highlights

The cluster reference is now immutable on the Database, Pooler, Publication, Subscription, and ScheduledBackup resources. Pointing one of these objects at a different cluster has no well-defined semantics and previously left the controllers in an inconsistent state; the update is now rejected at the API server via a CEL validation rule. (#10743)

Features

DescriptionAddresses
Primary Lease for safe primary election

introduced a Kubernetes Lease object (named after the cluster) that acts as a mutex serializing primary promotion: the instance manager must hold the lease before acting as primary and releases it on clean shutdown so replicas can promote without waiting for the full TTL. Timings are configurable via the new .spec.primaryLease stanza. The lease is a promotion gate, not a fence — primary isolation remains responsible for fencing.

#10627
DatabaseRole CRD for declarative role management

introduced a DatabaseRole custom resource that manages a PostgreSQL role as a standalone Kubernetes object, instead of declaring it inline in theCluster's .spec.managed.roles stanza. Each role gets its own lifecycle, status, and RBAC, which suits GitOps workflows and lets role definitions live next to the applications that own them. The spec reuses the sameRoleConfiguration structure as the inline method, so migrating a role is a matter of moving the stanza into its own manifest. AdatabaseRoleReclaimPolicy field (retain, the default, or delete) controls what happens to the role when the resource is deleted, mirroring persistent volumes.

#6155
TLS client certificates for declarative roles

a DatabaseRole can now include a clientCertificate block to have the operator automatically generate and renew a TLS client certificate, signed by the cluster's client CA and stored in a <databaserole-name>-client-certSecret. This enables password-free PostgreSQL cert authentication; the Secret is cleaned up when the feature is disabled or the DatabaseRole is deleted.

#10896
PgBouncer image management via image catalogs

the Pooler resource can now reference an entry in an ImageCatalog orClusterImageCatalog through the new spec.pgbouncer.imageCatalogRef field, centralizing PgBouncer image management. When a catalog entry is updated, all referencing Poolers are automatically reconciled and roll out the new image without any change to their spec. The resolved image is reported instatus.image, and a new status.phase (active, paused, inactive, orfailed) — also surfaced as a Phase column in kubectl get pooler — summarizes the lifecycle.

#10568

Enhancements

DescriptionAddresses
Enabled pg_upgrade in-place major upgrades to PostgreSQL 19 or later for clusters that use Image Volume extensions, building on the extension-path support added to pg_upgrade in PostgreSQL 19.

During the upgrade Job, the source- and target-version extension images are mounted side by side, so the old server keeps its libraries and a failed upgrade reverts cleanly.

#10366
Added TLS support for the Pooler metrics endpoint via.spec.monitoring.tls.enabled.

When enabled, the metrics server is served over HTTPS, reusing the certificate and key from .spec.pgbouncer.clientTLSSecret and reloading it on every handshake to support rotation without a restart; the generated PodMonitorscrapes over https accordingly.

#10466
Added a label selector to the Cluster scale subresource (status.selector), making a Cluster a valid targetRef for the Vertical Pod Autoscaler (VPA) and Horizontal Pod Autoscaler (HPA), which can now map a Cluster to its instance pods.

Contributed by @sebv004.

#8996
The operator now emits a `Warning` `PrimaryStatusCheckFailed` event on the `Cluster` when the primary pod is `Ready` from the kubelet perspective but the operator's `/pg/status` check fails and failover is deferred, giving users visibility into the deferral via `kubectl describe cluster`.#10509
Added the ENABLE_WEBHOOK_NAMESPACE_SUFFIX flag, which suffixes the operator's webhook configuration names with -<OPERATOR_NAMESPACE> so that multiple operator instances can coexist on the same cluster.

The operator only looks up these configurations; users must create and maintain them. Contributed by @maxlengdell.

#10420
The operator now reloads a CNPG-i plugin automatically when its pods are rolled: it watches the `EndpointSlices` backing plugin `Services` and re-enqueues every cluster using the plugin once the new pods become `Ready`, so an upgraded plugin is picked up without waiting for the next resync.#10836
Instance serial numbers are now assigned by reusing the lowest free slot among existing instance names, instead of always incrementing a global counter.

Pod and PVC names stay stable across instance recreation (for example, an instance recreated after a node drain comes back with the same name), and serials freed by deleted instances are reclaimed. A new Initialized cluster condition reports whether the cluster has completed its first bootstrap, andstatus.latestGeneratedNode is deprecated: it is no longer written, but is preserved on the CRD for backward compatibility.

#10548
Defaulting and validation now run during reconciliation as a fallback when admission webhooks are unavailable, or configured to ignore failures, so the operator no longer reconciles invalid or incomplete specs.

Missing defaults are applied directly, and validation failures are surfaced in the resource status instead of failing silently later.

#10874

Security Fixes

DescriptionAddresses
search_path pinning on operator-issued connections

a database owner could plant overloaded built-in operators in the publicschema and alter the search_path so that operator introspection probes, running as the cluster superuser, resolved those overloads before pg_catalog— a CWE-426 privilege-escalation chain (same class as CVE-2018-1058) that could lead to in-pod RCE via COPY ... FROM PROGRAM. The operator now pinssearch_path = pg_catalog, public, pg_temp on every pooled connection so it ships in the startup message and takes precedence over tenant-controlled defaults.

#10774
Authenticated operator-to-instance-manager calls

the instance manager's remote webserver previously accepted unauthenticated requests, so any process on the pod network could trigger backups, instance manager upgrades, or WAL archival. The operator now generates an in-memory ECDSA P-256 client certificate at startup and reconciles its SHA-256 fingerprint into the cluster status; the instance manager rejects requests to sensitive endpoints that do not present a matching certificate.

#10579
Operator-side SCRAM-SHA-256 password encoding

the operator now SCRAM-SHA-256 encodes cleartext role passwords before issuingCREATE/ALTER ROLE ... PASSWORD, so the literal PostgreSQL parses (and that extensions such as pg_stat_statements or pgaudit may capture) is the SCRAM verifier rather than the cleartext secret. Pre-hashed (MD5 or SCRAM) values are forwarded unchanged, and the per-Secret annotationk8s.enterprisedb.io/passwordPassthrough: "enabled" opts out.

#10724

Changes

DescriptionAddresses
Added support for Kubernetes 1.36.#10900
Updated the default PostgreSQL version to 18.4.#10719
Updated the Kubernetes versions used to test the operator on public cloud providers.#10720, #10563

Bug Fixes

DescriptionAddresses
Fixed declarative `Database`, `Publication`, and `Subscription` objects reporting a stale primary-side status forever after their cluster was demoted to a replica; the controller now re-checks the replica condition and watches the `Cluster` so a demotion is detected promptly.#10871
Fixed non-sequential pod names (for example `-1`, `-3`) caused by the instance serial counter being advanced before the corresponding `Job` and PVCs were created; the bump is now persisted only after those resources exist.#10491
Fixed deletion of a Database, Publication, or Subscription getting stuck in Terminating on a replica cluster, where the replica gate ran before the finalizer reconciler and the finalizer was never released.

On a replica the PostgreSQL object is left to the primary cluster.

#10853
Fixed a conflicting duplicate `Database` or `Subscription` with a `delete` reclaim policy dropping the PostgreSQL object owned by the surviving CR; the drop is now gated on a recorded reconciliation.#10870
Fixed the postgres superuser being left locked out after superuser access was disabled and then re-enabled, because the cached secret version was not invalidated and the password was never re-applied.

Diagnosed by @mhartmann-jaconi.

#10834
Fixed backups getting stuck in the `started` phase when the instance manager running them was restarted (for example by the in-place upgrade following an operator upgrade) before the backup reached `running`; the reconciliation is now rescheduled so the lost session is detected.#10859
Fixed exec/attach streaming to negotiate WebSocket with a SPDY fallback, restoring compatibility both with Kubernetes versions that have removed SPDY and with platforms such as OpenShift that reject WebSocket exec upgrades.

Contributed by @bartscheers.

#10876, #10933
Fixed resource leaks when concurrent Backup objects raced: backups now run in strict creation-time order, so an already-executing backup is never preempted by a newer one and its replication slot and PostgreSQL session are no longer orphaned on the primary.

Contributed by @GabriFedi97.

#10747
Fixed role reconciliation clearing the password on a PostgreSQL role when the referenced Secret could not be fetched; the role is now left untouched until the Secret becomes available, and per-action errors are aggregated for better visibility.#10053
Fixed a bootstrap failure where a metrics-exporter setup error (commonly a duplicate-key race with the controller) rolled back streaming_replicacreation and wedged replica joins.

The metrics-exporter step now runs in a separate transaction. Contributed by @BlaiseAntony.

#10749
Fixed a `ScheduledBackup` controller loop that occurred when a `Backup` was created but its status patch never landed; the controller now adopts an existing `Backup` for the next iteration instead of looping on `AlreadyExists`.#10612
Fixed a nil-pointer panic when reconciling a `Pooler` whose `Cluster` has been deleted.#10667
Fixed bootstrap log handling so that all named log pipes (`postgres`, `postgres.csv`, and `postgres.json`) get consumers during `WithActiveInstance`, preventing regular files from being created in place of the named pipes.#10043
Fixed generation of invalid IPv6 URLs by wrapping the address in square brackets.

Contributed by @Infinoid.

#10682
Fixed an external cluster plugin still being treated as active when its configuration set `enabled: false`.#10932
Fixed a race during bootstrap recovery from an object store where the restore job could read a stale Cluster (primary not yet recorded and timeline still unset) and have its .history files rejected by the split-brain guard.

When this happened, recovery stopped at the base backup's timeline and silently dropped transactions committed on later timelines. History files are now allowed while the cluster timeline is unset. Contributed by @dennispidun.

#10818