How to deploy PostgreSQL on Kubernetes with StatefulSets

Mydbops
Aug 14, 2026
5
Mins to Read
All
How to deploy PostgreSQL on Kubernetes with StatefulSets
How to deploy PostgreSQL on Kubernetes with StatefulSets

Deploy PostgreSQL on Kubernetes with StatefulSets only when you are deliberately building the database control plane yourself. For a production PostgreSQL cluster in 2026, the safer default is a PostgreSQL operator that owns replication, failover, service discovery, and recovery while Kubernetes supplies compute, network, and persistent storage.

TL;DR

  • For PostgreSQL on Kubernetes in 2026, use an operator for HA; a bare StatefulSet is only a pod-and-volume primitive.
  • CloudNativePG manages database PVCs directly and does not use StatefulSets for persistence.
  • A backup is not complete until a new cluster restores from it and accepts application traffic.
  • Test primary loss, node drain, and restore before production; each exposes a different failure path.

Start with the right premise

A StatefulSet gives pods stable names and can create one PVC per pod. That is useful infrastructure, but it is not a PostgreSQL availability design. It does not decide which instance is primary, configure replication, prevent conflicting promotion, route writes after a failover, or prove that a backup can be restored.

That distinction changes the deployment sequence. Do not start by writing a three-replica StatefulSet manifest and then bolt on replication later. Start by deciding who operates PostgreSQL state. In a production cluster, use an operator such as CloudNativePG or operate Patroni-based PostgreSQL replication and its distributed configuration store yourself. That path is a database platform project, not a YAML shortcut.

In 2026, Mydbops treats Kubernetes PostgreSQL as a coupled system, not a manifest.

Pick the deployment model before the manifest

What you need before deployment

Do these checks before installing an operator or creating a database cluster:

  • A Kubernetes version supported by the selected operator. Check the operator release notes rather than assuming a managed Kubernetes version is compatible.
  • A block-storage class with known zone behavior, encryption settings, expansion policy, IOPS limits, and reclaim behavior.
  • Three failure domains if you expect three database instances to survive a node or zone event. Three pods on one node are not HA.
  • A private object-store destination or another supported backup target, plus credentials stored as Kubernetes secrets or workload identity.
  • A write endpoint, a read endpoint if the application uses replicas, and a clear connection-pooling design.
  • Prometheus-compatible metrics, PostgreSQL logs, Kubernetes events, and alert ownership before the first production write.
  • A recovery objective expressed in time. “We have backups” is not an RPO or RTO.

For regulated workloads, Mydbops recommends defining encryption, access control, audit logging, and backup retention before provisioning the first volume. Retrofitting those controls after data exists is a migration project.

Build the cluster around failure boundaries

1. Validate storage semantics

PostgreSQL expects durable, ordered writes. Confirm that the chosen CSI driver and volume type meet that requirement, then verify how the provider behaves during a node loss, a zone outage, volume expansion, and a detach/attach delay.

Do not copy an old kubernetes.io/aws-ebs StorageClass example into a 2026 cluster. Use the CSI driver supplied by the platform and check its current parameters. Set the PersistentVolume reclaim policy according to recovery policy, not convenience: Retain preserves the PV after its PVC is deleted; Delete allows the provider to remove it. Neither setting replaces backups.

Deleting a PostgreSQL pod does not itself delete its PVC. Validate PVC retention and PV reclaim behavior in a non-production cluster.

Exit condition: create, delete, and recreate a test workload; then confirm the intended data-retention behavior from the PVC and PV state, not from the manifest alone.

2. Create an operator-managed PostgreSQL cluster

Install the operator using its release-specific instructions, then define a Cluster resource. Start with three instances only if the Kubernetes topology and storage budget can place them independently.

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: payments-db
spec:
  instances: 3
  storage:
    size: 200Gi
    storageClass: postgres-ssd

Add capacity, placement, backup, security, monitoring, and PostgreSQL settings before production traffic. If this resource cannot reconcile cleanly, fix the cluster foundation first.

CloudNativePG maintains the write service as primary ownership changes. Your application should use the operator-managed read-write service, not a pod ordinal such as postgres-0. Binding the application to a pod name turns a successful failover into an application outage.

Exit condition: all instances report healthy, the write endpoint accepts transactions, and a read replica has measurable replay activity.

3. Place instances across real failure domains

A three-instance cluster protects availability only when the instances do not share the same point of failure. Use topology spread constraints or anti-affinity to distribute pods across nodes and, where supported, zones. Then confirm that the storage layer can attach the correct volume in the surviving location.

Do not use hard anti-affinity without checking node capacity. A scheduling rule that leaves a replica permanently Pending creates the appearance of HA while running short of quorum. Conversely, do not permit all replicas onto one node just to make the deployment green.

Mydbops uses the same test for both choices: cordon and drain one eligible node in a non-production environment, then inspect where each PostgreSQL instance lands and whether the cluster retains a writable primary.

Exit condition: one node drain leaves a writable primary and at least one healthy replica, with no manual pod deletion.

Topology Spread & Automated Node Drain Failover

Continuous loop: Primary node drain event triggering standby promotion and self-healing

Zone us-east-1a ● Node-01
● Primary (Instance-1)
✖ Drained / Offline
Standby (Sync)
k8s-node-zone-a
💾 csi-ebs-200Gi
Zone us-east-1b ● Node-02
Standby (Instance-2)
★ Promoted Primary
k8s-node-zone-b
💾 csi-ebs-200Gi
Zone us-east-1c ● Node-03
Standby (Instance-3)
k8s-node-zone-c
💾 csi-ebs-200Gi
● Phase 1: 3/3 Healthy | Writes Routed -> Zone us-east-1a
⚠️ Phase 2: Zone us-east-1a Drained → Instance-2 Promoted in Zone us-east-1b
🔄 Phase 3: Zone A Rejoined as Standby | Cluster Quorum Self-Healed

4. Set memory, CPU, and connection limits together

PostgreSQL memory settings cannot be sized independently from container memory. shared_buffers, per-session work memory, parallel workers, autovacuum, and connection count all compete inside the cgroup. A database that is stable on a VM can be OOM-killed in Kubernetes after a burst of concurrent queries.

Set requests from observed workload demand, then set limits only after confirming the database can run within them. Do not make CPU requests and limits identical merely to chase a QoS label; PostgreSQL workloads often need controlled CPU headroom. The correct values come from peak connection count, query concurrency, buffer-cache needs, and node capacity.

Use a pooler to bound application connections. Validate ProxySQL for PostgreSQL read/write splitting separately for read/write routing. Raising max_connections is a memory decision, not a scaling plan.

Exit condition: a load test reaches expected concurrency without connection exhaustion, throttling alerts, or OOM events.

PostgreSQL Memory & CGroup Safety Margin Visualizer

Prevent OOMKilled events by balancing shared buffers with work memory limits

Headroom
Active work_mem
shared_buffers
Status: Safe CGroup Memory Overhead

5. Design backup and recovery as separate controls

A storage snapshot can help recover a volume quickly. It does not replace PostgreSQL-aware backups, WAL archiving, or point-in-time recovery. The recovery design needs an object-store destination, retention window, base backups, continuous WAL availability, and a documented restore command path; see the Mydbops PostgreSQL disaster recovery guide for the underlying recovery model.

CloudNativePG supports backup and recovery workflows through supported methods and plugins. Pin the operator version and use its matching backup documentation.

The meaningful test is not whether a backup job reports success. Restore to a new namespace or cluster name, run integrity checks, connect with the application role, and measure elapsed time. Record the timestamp of the restored data to prove the recovery point objective.

Exit condition: a new cluster restores from the chosen backup path within the required RTO and reaches a measured RPO.

Continuous WAL Archiving & Recovery Pipeline

WAL streaming to object storage and continuous automated restore drills

🐘
Active Primary
Generates WAL
☁️
Object Store
S3 / GCS / Blob
🔄
Restored Target
Verified PITR
🔄 Continuous DR Drill Loop: Base Backup + PITR WAL Replay

6. Define failover behavior and application behavior

Failover has two parts: the database promotes a replica, then clients reconnect to the correct write endpoint. Test both. A promotion that completes in the operator status but leaves connection pools pinned to dead sockets is still an outage.

Run a controlled primary-pod deletion in non-production. Then run a node drain, because a node drain exercises scheduling and volume attachment differently. Finally, simulate the operational response to a stalled replica without forcing promotion. These are three distinct events, and each deserves its own runbook. For non-operator architectures, review automatic PostgreSQL failover with pg_auto_failover.

In 2026, the operational question is not “does automatic failover exist?” It is “what data loss window, promotion time, and reconnect behavior did we measure under our storage and network conditions?” Mydbops can help teams turn that question into a repeatable production-readiness drill rather than a launch-day assumption.

Exit condition: the team records primary promotion, write-service recovery, client reconnect time, replication state, and any lost transactions for each failure drill.

The operating checklist after go-live

Production PostgreSQL on Kubernetes needs ongoing database administration, not only Kubernetes health checks. Review these signals every day:

  • Replica replay delay and replication-slot growth.
  • WAL archive and retention status, the age of the last recoverable point, and failed backup jobs.
  • PVC capacity, inode pressure where applicable, storage latency, and volume expansion headroom.
  • Connection-pool saturation, active sessions, lock waits, slow queries, and checkpoint pressure.
  • Pod restarts, eviction events, CPU throttling, memory pressure, and placement drift.
  • Operator version, PostgreSQL version, extension version, and pending restart requirements.

Mydbops supports PostgreSQL teams that need the database layer watched alongside the Kubernetes layer. The distinction matters: Kubernetes can restart a pod while PostgreSQL is still falling behind on WAL archival, vacuum, or query latency.

Troubleshoot by symptom, not by pod status

The cluster has three Running pods but no safe write path

Check the operator cluster status, primary designation, replication state, and write service endpoints. Three Running pods only prove containers are alive. They do not prove a primary exists or that replicas are current enough to promote.

A replica stays Pending after adding anti-affinity

Inspect the scheduler events and node labels before weakening the placement policy. The fix is usually capacity or an incomplete zone topology, not removing the rule that prevents co-location.

The database restarts after a traffic spike

Compare container memory events with PostgreSQL logs, connection count, and memory settings. Treat OOMKilled as a sizing failure until the cgroup limit, active workload, and PostgreSQL memory budget reconcile.

A backup job succeeded but recovery fails

Separate backup creation from recovery validation. Confirm the base backup is present, WAL exists through the desired recovery point, credentials work from the restore namespace, and the target cluster uses a compatible configuration.

Writes fail after a primary failover

Inspect the application’s connection pool and DNS refresh behavior. The application must reconnect to the operator-managed write service; it should never treat a former primary pod name as a durable database endpoint.

Storage fills although the table size is stable

Measure WAL retention, replication-slot lag, archive backlog, index growth, and table bloat. Kubernetes reports a full PVC; PostgreSQL must identify which database artifact consumed it.

Tools and resources

  • CloudNativePG: an operator-managed model for PostgreSQL clusters, failover orchestration, services, and PVC lifecycle.
  • Kubernetes CSI storage: the driver and volume class that determine durability, attachment, expansion, and reclaim behavior.
  • PgBouncer: connection pooling that caps database sessions before a traffic spike turns into memory pressure.
  • PostgreSQL observability with Prometheus and Grafana: the telemetry layer for replay delay, WAL archival, connections, locks, and resource pressure.
  • A separate restore environment: the only place to prove that backup credentials, manifests, WAL, and application roles work together.

FAQ

Should I deploy PostgreSQL with a bare StatefulSet in 2026?

No, not for production HA. A bare StatefulSet provides stable pod identity and storage claims, but it does not manage PostgreSQL replication, primary election, failover, or recovery.

Does CloudNativePG use StatefulSets for PostgreSQL pods?

No. CloudNativePG manages PostgreSQL persistent volume claims directly through its Cluster resource rather than using StatefulSets for persistence.

Do three PostgreSQL pods guarantee high availability on Kubernetes?

No. Three pods only help when replicas are configured, placed across real failure domains, monitored, and able to promote safely after a primary or node failure.

Does deleting a PostgreSQL pod delete its data volume?

Usually no, because pod deletion and PVC deletion are separate lifecycle events. Verify the StatefulSet retention policy, PVC state, PV reclaim policy, and CSI behavior in your own cluster.

What backup is required for PostgreSQL on Kubernetes?

Use PostgreSQL-aware base backups with continuous WAL archiving and test restoration. Volume snapshots can complement this design but do not replace point-in-time recovery.

How do applications find the new primary after failover?

Applications should connect to the operator-managed read-write service. They must not use a pod ordinal or a static primary IP, because primary ownership changes during failover.

What should I test before putting PostgreSQL on Kubernetes into production?

Test primary loss, node drain, backup restoration, write-service reconnection, replica lag, and capacity under expected concurrency. Record the measured recovery point and recovery time for each drill.

Production-readiness test

The useful question is not whether PostgreSQL can run on Kubernetes in 2026. It can. The useful question is whether your team can restore a fresh cluster, route writes to it, and explain the last recoverable transaction timestamp without improvising. If the answer is no, the deployment is still in staging regardless of its pod count.

Production-readiness test

The deployment is not production-ready until your team can restore a fresh cluster, route writes to it, and state the last recoverable transaction timestamp.

No items found.

About the Author

Subscribe Now!

Subscribe here to get exclusive updates on upcoming webinars, meetups, and to receive instant updates on new database technologies.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.