Running databases on Kubernetes is controversial. The honest answer: it depends. With a mature operator, you gain failover, backup, and upgrade automation that you would hardly get with custom scripts.
CloudNativePG (CNPG) is one of the most complete operators for PostgreSQL.
Course seal: tested on Kubernetes 1.35 with CloudNativePG 1.24/1.25 and PostgreSQL 16.
What the operator delivers
- Cluster with primary + replicas.
- Services
-rw,-ro, and-r. - Managed streaming replication.
- Failover and switchover.
- Continuous backup (WAL) to S3/MinIO.
- Restore and point-in-time recovery.
- Prometheus metrics.
The Cluster
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: portal-postgres
namespace: portal
spec:
instances: 2
imageName: ghcr.io/cloudnative-pg/postgresql:16.4
storage:
size: 10Gi
storageClass: portal-nfs
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "2"
memory: 2Gi
monitoring:
enablePodMonitor: true
backup:
barmanObjectStore:
destinationPath: s3://portal-k8s-backup/
endpointURL: http://minio-s3:9000
s3Credentials:
accessKeyId:
name: portal-secrets
key: APP_STORAGE_S3_ACCESSKEY
secretAccessKey:
name: portal-secrets
key: APP_STORAGE_S3_SECRETKEY
wal:
compression: gzip
retentionPolicy: "14d"
Services created:
| Service | Usage |
|---|---|
portal-postgres-rw |
write (primary) |
portal-postgres-ro |
read (replicas) |
portal-postgres-r |
any instance |
The application should write to -rw and read from -ro (if it tolerates lag).
Failover and switchover
- Switchover: planned, no data loss.
- Failover: primary died; the operator promotes the most advanced replica.
CNPG uses replication and an instance manager. If the primary loses liveness, the operator initiates failover. The old primary must be fenced (stop writing). If it does not die, the cluster may get stuck in Failing over.
Real case: in a load test, the primary lost probes due to memory/CPU contention. Failover started, but the old pod got stuck in shutdown. Manually removing the pod allowed the replica promotion. The runbook is in the postmortem chapter.
Backup
CNPG backup has two parts:
- Continuous WAL archiving (low RPO).
- Scheduled base backup.
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: portal-postgres-daily
spec:
schedule: "0 2 * * *"
immediate: true
backupOwnerReference: self
cluster:
name: portal-postgres
Verify:
kubectl -n portal get backups.postgresql.cnpg.io
kubectl -n portal get scheduledbackup
A backup that has never been restored is not a backup. Do drills.
Restore
Restore to a new cluster (do not overwrite the current one):
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: portal-postgres-restore
spec:
instances: 1
storage:
size: 10Gi
bootstrap:
recovery:
source: portal-postgres
externalClusters:
- name: portal-postgres
barmanObjectStore:
destinationPath: s3://portal-k8s-backup/
endpointURL: http://minio-s3:9000
s3Credentials:
accessKeyId:
name: portal-secrets
key: APP_STORAGE_S3_ACCESSKEY
secretAccessKey:
name: portal-secrets
key: APP_STORAGE_S3_SECRETKEY
For PITR, add recoveryTarget with date/time. Validate the data before pointing the application to it.
Lab
- Inspect the cluster:
kubectl -n portal get cluster.postgresql.cnpg.io portal-postgres
kubectl -n portal get pods -l cnpg.io/cluster=portal-postgres -o wide
kubectl -n portal get svc | grep postgres
- See the primary and replicas:
kubectl -n portal get cluster.postgresql.cnpg.io portal-postgres \
-o jsonpath='{.status.currentPrimary}{"\n"}'
- Test failover in the lab:
kubectl -n portal delete pod <primario>
kubectl -n portal get pods -w
- Take a manual backup and watch it:
kubectl -n portal create -f backup.yaml
kubectl -n portal get backups.postgresql.cnpg.io -w
- Restore to a new cluster and validate with
psql.
Common errors
| Symptom | Cause |
|---|---|
Cluster does not become Ready |
PVC, storageClass, or secrets |
| Backup fails | S3 credentials, endpoint, or bucket |
Failing over stuck |
old primary did not finish shutdown |
| Replica lagging | IO/network or insufficient resources |
Application writes to -ro |
wrong service configuration |
| Restore does not start | incomplete backup/WAL or credentials |
Checklist
- [ ] Cluster with at least 2 instances.
- [ ] Anti-affinity between primary and replica.
- [ ] PDB and resources defined.
- [ ] Scheduled backup + WAL archiving.
- [ ] Restore tested on a new cluster.
- [ ] Monitoring and alerts for lag/failover.
- [ ] Failover and fencing runbook.
Glossary
- primary: write instance.
- replica: read/replication instance.
- failover: automatic promotion.
- switchover: planned switch.
- WAL: write-ahead log.
- PITR: point-in-time recovery.
- fencing: preventing the old primary from writing.
Next
Now that the database is covered, let's move to object storage: MinIO, erasure coding, buckets, and credentials.
Next: Kubernetes Course 16 — MinIO: erasure coding, buckets, and credentials
Keep reading
Read also

StorCLI - Complete Cheat Sheet
StorCLI is the official and modern tool for managing Broadcom/LSI RAID controllers. This guide covers essential commands, syntax and use cases.
12 min de leitura
TutoriaisBusiness Services in Nyverra Portal: Translating IT into Business
The business doesn't feel a server go down — it feels email stop. The Business Services (Service Mapping) module of Nyverra ITSM Portal connects services like Email, ERP or PACS to CMDB assets, revealing the real impact of each failure.
5 min de leitura
SegurançaRPO vs RTO: How to Define Before Buying Backup (and Why Most Companies Get It Wrong)
Most companies buy backup asking the wrong question: "how much does it cost?". The right question is: how much data can I lose (RPO) and how long can I be down (RTO). Discover how to define these numbers per system and avoid purchasing protection based on price, not on real need.
9 min de leitura
Comments
No comments yet
Be the first to share your thoughts on this article.

Leave a comment