Understanding and Using StatefulSets and Headless Services
CloudNativePG, Operators, CRDs, and PostgreSQL Clustering in Kubernetes
Let's quickly review how CloudNativePG (CNPG) implements PostgreSQL clustering in Kubernetes. First of all, before understanding how it works, we need to understand a concept usually used in Operators called Custom Resource Definitions (CRDs).
An Operator is a special type of controller that extends Kubernetes to manage complex applications as if they were native resources.
Normally, Kubernetes manages things like Pods, Deployments, and Services — all defined by standard YAML manifests. But many applications, like PostgreSQL or Kafka, need logic that goes beyond "start a Pod and keep it running." They require setup, scaling, replication, backup, and recovery — things Kubernetes doesn’t understand on its own.
That’s where Operators come in. An Operator encodes human operational knowledge — the playbook a database admin would normally follow — into software that runs inside the cluster. It continuously watches the current state of your application and reconciles it to match the desired state you’ve declared in YAML.
Our definition of an Operator is intentionally focused on databases because the concept itself is broad. The same idea applies to other complex applications as well: Redis, Kafka, Elasticsearch, Prometheus, Istio, and many more tools have Operators to manage their lifecycle. The Operator Framework is the starting point if you want to learn more about building Operators.
Under the hood, an Operator usually relies on Custom Resource Definitions (CRDs). A CRD lets you define new Kubernetes objects — for example, PostgresCluster or Backup — just like you already have Deployment or Service. Once the CRD is installed, you can create, list, and delete these custom resources using kubectl, and the Operator will react to them automatically.
So, while a regular Helm chart or a plain manifest installs an app once, an Operator manages that app’s entire lifecycle: provisioning, upgrades, failover, scaling, and backups — all declaratively, through CRDs.
In the case of PostgreSQL, when you install CloudNativePG, the Operator is deployed, and it adds CRDs like Cluster, Pooler, Backup, and ScheduledBackup. You then declare a Cluster manifest (replicas, storage, parameters), and the Operator continuously reconciles reality to match it — creating Pods, PVCs, Services, and keeping them healthy.
CloudNativePG automates the full lifecycle of PostgreSQL. It natively handles:
- High availability and failover.
- Setting up physical streaming replication between a primary and standbys.
- Monitoring their health.
- Promoting a new leader when needed—no external tools like Patroni are required.
The operator uses Kubernetes primitives effectively:
Each Pod has a stable identity and its own storage, while Services expose clear endpoints for writes (
primary) and reads (replicas); headless Services enable precise, per-Pod addressing.It integrates backups, WAL archiving, and point-in-time recovery (PITR) using a Barman-based system that continuously streams WAL files to object storage. Recovery creates a new cluster from backups and WAL rather than modifying the existing one in place.
Operational tasks such as tuning, scaling, and rolling updates are fully declarative: you describe the desired configuration in YAML, and the operator reconciles it safely using native Kubernetes primitives.
In short, you describe the PostgreSQL cluster in YAML, and CloudNativePG continuously maintains it—replication, failover, backups, and recovery all built in.
You can start testing this solution quickly using their local quickstart:
# Install version 1.27 of CloudNativePG
# --server-side enables server-side apply
kubectl apply --server-side -f \
https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.27/releases/cnpg-1.27.0.yaml
# Verify the operator is running
kubectl rollout status deployment \
-n cnpg-system cnpg-controller-manager
# Deploy a PostgreSQL cluster
kubectl apply -f - <
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example
namespace: postgres
spec:
instances: 3
bootstrap:
initdb: Cloud-Native Microservices With Kubernetes - 2nd Edition
A Comprehensive Guide to Building, Scaling, Deploying, Observing, and Managing Highly-Available Microservices in KubernetesEnroll now to unlock all content and receive all future updates for free.
