Skip to main content
PGO is built on the Kubernetes operator pattern: it watches custom resources you define, compares their desired state to the actual state in your cluster, and continuously reconciles any divergence. You declare what your PostgreSQL cluster should look like; PGO makes it so and keeps it that way. This page explains the components that make up that system — from the reconciler loop down to individual tools like Patroni and pgBackRest.

The operator pattern

PGO registers three controllers with the controller-runtime manager when it starts: Each controller uses a reconciler loop. When you create or update a custom resource, Kubernetes adds an event to the controller’s work queue. The reconciler reads the current state of the cluster, computes the desired state from the spec, and applies the necessary Kubernetes API calls to converge them. The operator runs with leader election (configurable via the PGO_CONTROLLER_LEASE_NAME environment variable) so that exactly one replica is active at any time. It uses coordination.k8s.io/leases for the lease and exposes a health probe on port 8081 and authenticated Prometheus metrics on port 8443.

PostgresCluster controller

The postgrescluster controller owns every Kubernetes object that belongs to a PostgresCluster. The ManagedReconciler function registers ownership of:
  • ConfigMap, Secret, Service, ServiceAccount, Endpoints
  • PersistentVolumeClaim
  • Deployment, StatefulSet
  • Job, CronJob
  • Role, RoleBinding
  • PodDisruptionBudget
Because each of these resources is owned by the PostgresCluster, deleting the PostgresCluster cascades to all its child objects through Kubernetes garbage collection.

Reconciliation order

The reconciler follows a strict ordering to ensure each subsystem is available before the next depends on it. Condensed from Reconcile() in controller.go:
1

Validation and defaults

Set defaults, detect OpenShift, verify required container images, warn if the selected PostgreSQL version is end-of-life, and validate standby configuration.
2

Root CA and TLS

Reconcile the cluster’s root certificate authority (reconcileRootCertificate). All subsequent TLS material derives from this CA.
3

Data source and volume preparation

Run directory-move Jobs if volumes need to be relocated (reconcileDirMoveJobs), observe existing PersistentVolumeClaims, and reconcile the data source for new clusters or in-place restores (reconcileDataSource). The reconciler returns early while these long-running Jobs are in progress.
4

Networking and RBAC

Reconcile the cluster pod Service, the primary Service, the replica Service, the Patroni leader lease Service, and the instance ServiceAccount with its Role and RoleBinding.
5

Patroni configuration

Generate pg_hba.conf rules and PostgreSQL parameters, then reconcile Patroni’s distributed configuration (stored in an Endpoints object) and dynamic configuration (applied live via the Patroni API).
6

Instance sets

Reconcile the StatefulSets that run PostgreSQL pods, including rolling updates and pod restart handling (reconcileInstanceSets, handlePatroniRestarts).
7

Databases and users

Create or update databases (reconcilePostgresDatabases), provision users and their credential Secrets (reconcilePostgresUsers), and apply any databaseInitSQL ConfigMap.
8

pgBackRest

Reconcile backup repositories, the pgBackRest repository host, scheduled backup CronJobs, and any in-progress restore or clone operations (reconcilePGBackRest).
9

Snapshots, pgBouncer, and monitoring

Reconcile VolumeSnapshots, the pgBouncer Deployment, and the pgMonitor exporter sidecar with its metrics configuration (reconcilePGBouncer, reconcilePGMonitorExporter).
10

pgAdmin

Optionally reconcile a pgAdmin Deployment and its configuration (reconcilePGAdmin).
At the end of every successful reconciliation cycle the controller updates status.observedGeneration to match the current metadata.generation. Status conditions (Progressing, ProxyAvailable, PersistentVolumeResizing) provide structured observability into what the operator is doing.
You can pause reconciliation at any time by setting spec.paused: true in the PostgresCluster. The controller sets a Progressing=False/Paused condition and stops applying changes until you remove the field.

High availability with Patroni

PGO uses Patroni for high availability. Patroni provides distributed consensus-based leader election: each PostgreSQL instance runs a Patroni agent that participates in a consensus protocol (backed by a Kubernetes Endpoints object acting as a DCS) to elect and maintain a single primary. Key behaviors:
  • Automatic failover: if the primary becomes unavailable, Patroni promotes the most up-to-date replica within seconds.
  • Self-healing primaries: after a failed primary recovers, Patroni automatically brings it back as a replica and reintegrates it into the cluster.
  • Synchronous replication: set spec.patroni to configure synchronous standby names, ensuring no transactions are lost during failover.
  • Switchover: PGO exposes reconcilePatroniSwitchover so you can trigger a controlled switchover by annotating the PostgresCluster.
  • Standby clusters: set spec.standby.enabled: true with either a repoName (WAL archive) or a host (streaming replication) to create a read-only standby cluster that can be promoted independently.
The Patroni leader Service (patroniLeaderService) always routes to the current primary. Applications should connect through this Service to avoid write-split scenarios.

Backups and restore with pgBackRest

pgBackRest handles all backup and restore operations. PGO reconciles a dedicated pgBackRest repository host pod and up to four backup repositories in spec.backups.pgbackrest.repos. Supported repository backends:
Each repository can be independently scheduled for full, incremental, or differential backups via CronJobs. PGO also supports efficient delta restores and cloning an existing cluster into a new PostgresCluster by referencing a dataSource.postgresCluster or a cloud-based dataSource.pgbackrest source.
Configure multiple repositories to store backups in more than one location simultaneously, for geographic redundancy or different retention policies.

Connection pooling with pgBouncer

Enable pgBouncer by adding spec.proxy.pgBouncer: {} to your PostgresCluster. PGO reconciles a pgBouncer Deployment (reconcilePGBouncer) that sits in front of the PostgreSQL primary. pgBouncer maintains a pool of server-side connections and serves multiple application clients from that pool, dramatically reducing connection overhead for applications that open many short-lived connections. PGO automatically rotates pgBouncer’s TLS certificate and keeps its configuration synchronized with the current primary’s address as Patroni performs failovers. The pgBouncer connection URI is stored in the pgbouncer-uri key of the user credential Secret. You can set resource requests and limits, replica count, custom configuration, and sidecar containers (via the PGBouncerSidecars feature gate) in spec.proxy.pgBouncer.

Monitoring with pgMonitor

PGO integrates pgMonitor for PostgreSQL metrics. When monitoring is enabled, PGO runs a postgres_exporter sidecar on each PostgreSQL instance pod. The exporter exposes standard PostgreSQL metrics in Prometheus format and is configured with query files managed through a ConfigMap (reconcileExporterQueriesConfig). The monitoring credentials are stored in a dedicated Secret (reconcileMonitoringSecret) separate from the application user Secrets. To enable monitoring, add spec.monitoring.pgmonitor to your PostgresCluster:
Enable the AppendCustomQueries feature gate to append your own SQL queries to the default pgMonitor query set.

Custom resource definitions

PGO ships three CRDs under the postgres-operator.crunchydata.com API group:

PostgresCluster

The primary CRD. Defines the full desired state of a PostgreSQL cluster. Key spec fields from postgrescluster_types.go:

PGUpgrade

Controls in-place major version upgrades. The PGUpgrade resource instructs PGO to run pg_upgrade across instance pods and then bring the cluster back up on the new PostgreSQL version. See the PGUpgrade reference for details.

PGAdmin

The standalone PGAdmin CRD (managed by standalone_pgadmin.ManagedReconciler) deploys a pgAdmin 4 instance that can connect to multiple PostgresCluster resources. This is distinct from the embedded spec.userInterface.pgAdmin field, which scopes pgAdmin to a single cluster. See the PGAdmin reference for details.

Feature gates

PGO uses a Kubernetes-style feature gate system to control the availability of features that are in Alpha or Beta maturity. Feature gates are set at operator startup via the PGO_FEATURE_GATES environment variable using the format Feature1=true,Feature2=false. The full gate list from internal/feature/features.go:
Alpha features are disabled by default. Beta features are enabled by default. You can override any gate at operator startup. See the feature gates reference for full documentation.

Namespace modes

By default PGO watches all namespaces in the cluster. You can restrict it to a single namespace using the PGO_TARGET_NAMESPACE environment variable, or to a set of namespaces using PGO_TARGET_NAMESPACES (comma-separated). This is configured in the Kustomize install overlays.

Further reading

Install with Kustomize

Step-by-step installation using the official Kustomize overlays.

High availability guide

Configure replicas, synchronous replication, and standby clusters.

Backups guide

Set up pgBackRest repositories and scheduled backup policies.

Feature gates reference

Full reference for all available PGO feature gates and how to configure them.