Installation and upgrades
Installation on Kubernetes
Directly using the operator manifest
The operator can be installed like any other resource in Kubernetes,
through a YAML manifest applied via kubectl .
You can install the latest operator manifest
for this minor release as follows:
kubectl apply --server-side -f \
https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.30/releases/cnpg-1.30.0.yaml
You can verify that with:
kubectl rollout status deployment \
-n cnpg-system cnpg-controller-manager
Using the cnpg plugin for kubectl
You can use the cnpg plugin to override the default configuration
options that are in the static manifests.
For example, to generate the default latest manifest but change the watch namespaces to only be a specific namespace, you could run:
kubectl cnpg install generate \
--watch-namespace "specific-namespace" \
> cnpg_for_specific_namespace.yaml
Please refer to postgresql.cnpg.io/v1 documentation for a more comprehensive example.
Warning
If you are deploying CloudNativePG on GKE and get an error (... failed to call webhook... ), be aware that by default traffic between worker nodes and control plane is blocked by the firewall except for a few specific ports, as explained in the official docs
and by this issue . You’ll need to either change the
targetPort in the webhook service, to be one of the allowed ones, or
open the webhooks’ port (9443 ) on the firewall.
Testing the latest development snapshot
If you want to test or evaluate the latest development snapshot of CloudNativePG before the next official patch release, you can download the manifests from the cloudnative-pg/artifacts
which provides easy access to the current trunk (main) as well as to each supported release.
For example, you can install the latest snapshot of the operator with:
curl -sSfL \
https://raw.githubusercontent.com/cloudnative-pg/artifacts/main/manifests/operator-manifest.yaml | \
kubectl apply --server-side -f -
If you are instead looking for the latest snapshot of the operator for this specific minor release, you can just run:
curl -sSfL \
https://raw.githubusercontent.com/cloudnative-pg/artifacts/release-1.30/manifests/operator-manifest.yaml | \
kubectl apply --server-side -f -
Note
Snapshots are not supported by the CloudNativePG Community, and are not intended for use in production.
Using the Helm Chart
The operator can be installed using the provided Helm chart .
Using OLM
CloudNativePG can also be installed via the Operator Lifecycle Manager (OLM)
directly from OperatorHub.io .
For deployments on Red Hat OpenShift, EDB offers and fully supports a certified version of CloudNativePG, available through the Red Hat OpenShift Container Platform .
Details about the deployment
In Kubernetes, the operator is by default installed in the
cnpg-system namespace as a Kubernetes Deployment . The name of
this deployment depends on the installation method. When installed
through the manifest or the cnpg plugin, it is called
cnpg-controller-manager by default. When installed via Helm, the
default name is cnpg-cloudnative-pg .
Note
With Helm you can customize the name of the deployment via the fullnameOverride field in the _"values.yaml"_ file .
You can get more information using the describe command in
kubectl :
$ kubectl get deployments -n cnpg-system
NAME READY UP-TO-DATE AVAILABLE AGE
<deployment-name> 1/1 1 1 18m
kubectl describe deploy \
-n cnpg-system \
<deployment-name>
As with any Deployment, it sits on top of a ReplicaSet and supports rolling upgrades. The default configuration of the CloudNativePG operator comes with a Deployment of a single replica, which is suitable for most installations. In case the node where the pod is running is not reachable anymore, the pod will be rescheduled on another node.
If you require high availability at the operator level, it is possible to specify multiple replicas in the Deployment configuration - given that the operator supports leader election. Also, you can take advantage of taints and tolerations to make sure that the operator does not run on the same nodes where the actual PostgreSQL clusters are running (this might even include the control plane for self-managed Kubernetes installations).
Note
You can change the default behavior of the operator by overriding some default options. For more information, please refer to the Operator configuration section.
Upgrades
Note
Please carefully read the Release notes for 1.14.0 and earlier
before performing an upgrade as some versions might require extra steps.
Upgrading CloudNativePG operator is a two-step process:
upgrade the controller and the related Kubernetes resources
upgrade the instance manager running in every PostgreSQL pod
Unless differently stated in the release notes, the first step is normally done by applying the manifest of the newer version for plain Kubernetes installations, or using the native package manager of the used distribution (please follow the instructions in the above sections).
The second step is automatically triggered after updating the
controller. By default, this initiates a rolling update of every
deployed PostgreSQL cluster, upgrading one instance at a time to use the
new instance manager. The rolling update concludes with a switchover,
which is governed by the primaryUpdateStrategy option. The default
value, unsupervised , completes the switchover automatically. If set
to supervised , the user must manually promote the new primary
instance using the cnpg plugin for kubectl .
Note
This process is discussed in-depth on the Rolling updates page.
Note
In case primaryUpdateStrategy is set to the default value of unsupervised , an upgrade of the operator will trigger a switchover on your PostgreSQL cluster, causing a (normally negligible) downtime. If your PostgreSQL Cluster has only one instance, the instance will be automatically restarted as supervised value is not supported for primaryUpdateStrategy . In either case, your applications will have to reconnect to PostgreSQL.
The default rolling update behavior can be replaced with in-place updates of the instance manager. This approach does not require a restart of the PostgreSQL instance, thereby avoiding a switchover within the cluster. This feature, which is disabled by default, is described in detail below.
Spread Upgrades
By default, all PostgreSQL clusters are rolled out simultaneously, which may lead to a spike in resource usage, especially when managing multiple clusters. CloudNativePG provides two configuration options at the operator level
that allow you to introduce delays between cluster roll-outs or even between instances within the same cluster, helping to distribute resource usage over time:
CLUSTERS_ROLLOUT_DELAY: Defines the number of seconds to wait between roll-outs of different PostgreSQL clusters (default:0).INSTANCES_ROLLOUT_DELAY: Defines the number of seconds to wait between roll-outs of individual instances within the same PostgreSQL cluster (default:0).
In-place updates of the instance manager
By default, CloudNativePG issues a rolling update of the cluster every time the operator is updated. The new instance manager shipped with the operator is added to each PostgreSQL pod via an init container.
However, this behavior can be changed via configuration to enable in-place updates of the instance manager, which is the PID 1 process that keeps the container alive.
Internally, each instance manager in CloudNativePG supports the injection of a new executable that replaces the existing one after successfully completing an integrity verification phase and gracefully terminating all internal processes. Upon restarting with the new binary, the instance manager seamlessly adopts the already running postmaster.
As a result, the PostgreSQL process is unaffected by the update, refraining from the need to perform a switchover. The other side of the coin, is that the Pod is changed after the start, breaking the pure concept of immutability.
You can enable this feature by setting the
ENABLE_INSTANCE_MANAGER_INPLACE_UPDATES environment variable to
'true' in the Operator configuration .
The in-place upgrade process will not change the init container image inside the Pods. Therefore, the Pod definition will not reflect the current version of the operator.
Compatibility among versions
CloudNativePG follows semantic versioning. Every release of the operator within the same API version is compatible with the previous one. The current API version is v1, corresponding to versions 1.x.y of the operator.
In addition to new features, new versions of the operator contain bug fixes and stability enhancements. Because of this, we strongly encourage users to upgrade to the latest version of the operator, as each version is released in order to maintain the most secure and stable Postgres environment.
CloudNativePG currently releases new versions of the operator at least monthly. If you are unable to apply updates as each version becomes available, we recommend upgrading through each version in sequential order to come current periodically and not skipping versions.
The Release notes for 1.14.0 and earlier page contains a detailed list of the changes introduced in every released version of CloudNativePG, and it must be read before upgrading to a newer version of the software.
Most versions are directly upgradable and in that case, applying the newer manifest for plain Kubernetes installations or using the native package manager of the chosen distribution is enough.
When versions are not directly upgradable, the old version needs to be removed before installing the new one. This won’t affect user data but only the operator itself.
Upgrading to 1.30.0, 1.29.2, or 1.28.4
Note
We strongly recommend that all CloudNativePG users upgrade to version 1.30.0, or at least to the latest stable version of your current minor release (e.g., 1.29.2 or 1.28.4).
These releases introduce changes worth reviewing before you upgrade. Two
are security changes that apply to 1.30.0, 1.29.2, and 1.28.4:
operator-side password encoding (CVE-2026-55765 ) and
search_path hardening (CVE-2026-55769 ). The other three are new
in 1.30.0 only: the DatabaseRole resource for declarative role
management, safe primary election via a per-cluster Lease, and
operator-to-instance authentication on the instance manager’s status
port (GHSA-7qwx-x8ff-3px9 ). In addition, if you are upgrading from
a release older than 1.29.1 or 1.28.3, the metrics-exporter
privilege separation from CVE-2026-44477 also applies. Each is
covered in its own subsection below.
Operator-side password encoding (CVE-2026-55765 )
Starting from versions 1.30.0, 1.29.2, and 1.28.4, for security reasons
(CVE-2026-55765 / GHSA-w3gf-xc94-wvmj ), CloudNativePG
SCRAM-SHA-256 encodes role passwords operator-side (client-side from
PostgreSQL’s point of view) before issuing CREATE /ALTER ROLE
statements. As a result, the literal that reaches the PostgreSQL parser
(and that extensions such as pg_stat_statements or pgaudit may
observe) is the same hash that ends up in pg_authid.rolpassword ,
never the cleartext secret. The encoding is applied to every basic-auth
Secret the operator consumes: the postgres superuser secret, the
application-user secret, and any managed-role password secret. Passwords
already supplied in MD5 or SCRAM-SHA-256 shadow form are passed through
unchanged.
Since PostgreSQL 14 , password_encryption defaults to
scram-sha-256 , so we do not expect existing installations to be
affected by this change.
If your cluster has explicitly overridden password_encryption to a
value other than scram-sha-256 (for example, md5 ) and you want
PostgreSQL (not the operator) to decide how the password is hashed, opt
out by setting the annotation cnpg.io/passwordPassthrough: "enabled"
on each basic-auth Secret the operator consumes. The operator will
then forward the password value verbatim, and PostgreSQL will encode it
according to its own password_encryption GUC.
Warning
The cnpg.io/passwordPassthrough annotation must be set on the basic-auth Secret itself, not on the Cluster resource. Placing it on the Cluster has no effect, and the operator will continue to apply SCRAM-SHA-256 encoding to the password before sending it to PostgreSQL.
Warning
With cnpg.io/passwordPassthrough: "enabled" the operator forwards the Secret's password value verbatim. If that value is cleartext, as is common on password_encryption = md5 clusters, extensions such as pg_stat_statements or pgaudit will observe it.
See Opting out of operator-side encoding
for details.
search_path hardening (CVE-2026-55769 )
Also starting from versions 1.30.0, 1.29.2, and 1.28.4, for security
reasons (CVE-2026-55769 / GHSA-x8c2-3p4r-v9r6 ), CloudNativePG
pins the search_path to a fixed pg_catalog, public, pg_temp on
every connection it opens to PostgreSQL, so that a tenant-controlled
ALTER DATABASE /ALTER ROLE setting can no longer influence how
operator-issued queries resolve unqualified object names. The
SECURITY DEFINER lookup function used by the PgBouncer integration
is recreated automatically with its own pinned search_path during
the first reconciliation after the upgrade. See Schema resolution and `search_path hardening <Schema resolution and search_path hardening>`
for the rationale.
This change also affects custom monitoring queries, which now run inside
a transaction whose search_path is pinned to
pg_catalog, public, pg_temp . If any of your custom metrics
reference objects that live in other user-defined schemas through an
unqualified name, schema-qualify them (for example myschema.mytable
) so they keep resolving after the upgrade. User-authored bootstrap
(postInit* ) and logical-import post-import SQL are unaffected: they
continue to run with the standard "$user", public resolution.
Declarative role management with the DatabaseRole resource
Starting from version 1.30.0, you can also manage a PostgreSQL role as a
standalone DatabaseRole resource instead of declaring it inline in the
Cluster’s .spec.managed.roles stanza. This is an opt-in enhancement
and requires no action on upgrade: existing inline roles keep working
unchanged, and the inline managed.roles method remains fully
supported.
To move an existing inline role under a DatabaseRole without
disruption, create the DatabaseRole first and only then remove the
matching entry from .spec.managed.roles . While both exist the
Cluster spec always takes precedence, so management is handed over only
once the inline entry is gone. See Migrating from inline managed roles to a `DatabaseRole <Migrating from inline managed roles to a DatabaseRole>`
for the full procedure.
A DatabaseRole does not support ensure: absent : where the
inline managed.roles stanza drops a role by setting
ensure: absent , a DatabaseRole instead relies on the
databaseRoleReclaimPolicy field. Delete the resource with
databaseRoleReclaimPolicy: delete to drop the role from PostgreSQL,
or keep the default retain to leave the role in place.
Safe primary election
Starting from version 1.30.0, CloudNativePG coordinates primary
promotion through a per-cluster Kubernetes Lease , ensuring that at
most one instance promotes itself at any given time. The behavior is
enabled automatically and requires no configuration; the lease timings
can optionally be tuned via .spec.primaryLease (see Safe primary election
).
Warning
This feature requires the operator to manage Lease objects in the coordination.k8s.io API group. The bundled operator manifest and the Helm chart grant the required permissions automatically. If you manage the operator's RBAC yourself, you must add the create , get , list , update and watch verbs on leases in the coordination.k8s.io API group before upgrading, otherwise primaries will be unable to promote.
Operator-to-instance authentication (GHSA-7qwx-x8ff-3px9 )
Starting from version 1.30.0, the operator authenticates its calls to
the sensitive endpoints of the instance manager’s status port (backup,
pg_controldata , partial WAL archive, and instance-manager upgrade)
by pinning an in-memory client certificate. This is enabled
automatically and requires no configuration. Status, health, and probe
endpoints remain unauthenticated. This hardening is not backported; on
releases earlier than 1.30.0, continue to restrict the status port (TCP
8000) with a NetworkPolicy , which remains its security boundary
there. See Operator-to-instance authentication (`GHSA-7qwx-x8ff-3px9 ) <Operator-to-instance authentication (GHSA-7qwx-x8ff-3px9 )>`
for details.
Warning
This protection has a hard requirement: the status port must be served over TLS, which has been the default since v1.24. Instances created by an operator older than v1.24 serve the status port over plain HTTP; once their instance manager is upgraded to 1.30.0 the operator can no longer authenticate to them and every call to the protected endpoints is permanently rejected with 401 Unauthorized . If you still run such instances, perform a rolling update so their Pods are recreated with a TLS-enabled status port. Instances created by v1.24 or later are unaffected.
Metrics exporter privilege separation (CVE-2026-44477 )
This applies only if you are upgrading from a release older than 1.29.1 or 1.28.3 (for example 1.29.0, 1.28.2, or any earlier version); installations already on 1.29.1, 1.28.3, or later already have this change.
The fix for CVE-2026-44477 / GHSA-423p-g724-fr39 makes the
metrics exporter authenticate as a dedicated cnpg_metrics_exporter
role with pg_monitor privileges only, instead of the postgres
superuser.
Custom monitoring queries that read user-owned tables, or use
target_databases: '*' against databases where PUBLIC CONNECT
has been revoked, need explicit GRANT statements to
cnpg_metrics_exporter . See Custom query privileges and safety
and Manually creating the metrics exporter role
in the monitoring documentation.
Verifying release assets
CloudNativePG cryptographically signs all official release assets. Verifying these signatures ensures the assets originate from the official repository and were published through our automated release workflow.
Note
Refer to the Release Integrity and Supply Chain
for more information.
Prerequisites
Signature verification: cosign CLI
SBOM and Provenance: Docker Buildx
(included in Docker Desktop and modern Docker versions)
Verifying the Operator YAML Deployment
When installing via a direct YAML manifest, you should verify the
manifest file using the corresponding bundle (the .sigstore.json
file) provided on the GitHub Release page .
Run the following command:
cosign verify-blob \
cnpg-{version}.yaml \
--bundle cnpg-{version}.sigstore.json \
--certificate-identity-regexp "^https://github.com/cloudnative-pg/cloudnative-pg/.github/workflows/release-publish.yml@refs/tags/v" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
Verifying SLSA provenance
To verify a release binary, download both the artifact and the
provenance file (multiple.intoto.jsonl ) from the GitHub release ,
then run:
slsa-verifier verify-artifact <ARTIFACT> \
--provenance-path multiple.intoto.jsonl \
--source-uri github.com/cloudnative-pg/cloudnative-pg
Verifying the operator container images
Run the following command to verify the signature of the CloudNativePG operator images:
cosign verify ghcr.io/cloudnative-pg/cloudnative-pg:{tag} \
--certificate-identity-regexp="^https://github.com/cloudnative-pg/cloudnative-pg/.github/workflows/release-publish.yml@refs/tags/v" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com"
We provide OCI attestations for full transparency. To inspect the
Software Bill of Materials (SBOM) or build provenance, use the
docker buildx imagetools command:
To view the Software Bill of Materials (SBOM) in SPDX format:
docker buildx imagetools inspect ghcr.io/cloudnative-pg/cloudnative-pg:{tag} \
--format {{ json (index .SBOM "linux/amd64").SPDX }}
To inspect the SLSA Provenance (build details):
docker buildx imagetools inspect ghcr.io/cloudnative-pg/cloudnative-pg:{tag} \
--format {{ json (index .Provenance "linux/amd64").SLSA }}
Note
Refer to Verifying SLSA provenance
for SLSA Build Level 3 compliance verification.
Verifying PostgreSQL operand images
CloudNativePG maintains container images for all supported PostgreSQL versions as part of the postgres-containers
(also called operand images).
To verify the signature of a specific operand image:
cosign verify ghcr.io/cloudnative-pg/postgresql:{tag} \
--certificate-identity-regexp="^https://github.com/cloudnative-pg/postgres-containers/" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com"
To view the Software Bill of Materials (SBOM) in SPDX format:
docker buildx imagetools inspect ghcr.io/cloudnative-pg/postgresql:{tag} \
--format {{ json (index .SBOM "linux/amd64").SPDX }}
To inspect the SLSA Provenance (Build details):
docker buildx imagetools inspect ghcr.io/cloudnative-pg/postgresql:{tag} \
--format {{ json (index .Provenance "linux/amd64").SLSA }}