Skip to main content
Version: 1.30

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/main/releases/cnpg-1.30.0-rc1.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 "cnpg plugin" 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 -
Important

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).

Operator configuration

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

Important

Please carefully read the release notes before performing an upgrade as some versions might require extra steps.

Upgrading CloudNativePG operator is a two-step process:

  1. upgrade the controller and the related Kubernetes resources
  2. 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.

Rolling updates

This process is discussed in-depth on the Rolling Updates page.

Important

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 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

Important

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 and search_path hardening. 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. 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

Starting from versions 1.30.0, 1.29.2, and 1.28.4, for security reasons, 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

Also starting from versions 1.30.0, 1.29.2, and 1.28.4, for security reasons, 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 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 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

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. See Operator-to-instance authentication 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.

info

Refer to the "Release integrity and supply chain" section 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 }}'
info

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 project (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 }}'