A DBA’s Guide to Declarative Postgres on Kubernetes
For a traditional DBA, the jump to Kubernetes often feels like losing your safety net. No more SSH sessions, no more direct edits to /var/lib/pgsql/data/postgresql.conf, and no more quick pg_ctl reload after a buffer tweak. The environment is ephemeral, and any manual change inside a container is generally forbidden under GitOps principles.
This is where CloudNativePG (CNPG) steps in. It translates Kubernetes-native YAML into production-ready Postgres configurations, replacing the imperative file editing workflow with something arguably better: a declarative, version-controlled operation model.
The Container-Age Mindset
The core shift is from imperative to declarative management. Instead of executing commands and editing files to reach a desired state, you define that state in a manifest. The operator—CNPG—then ensures the live cluster matches your specification. For Postgres specifically, this means your entire configuration strategy moves from a terminal to a spec.postgresql block in a Custom Resource Definition (CRD).
| Action | Traditional VM Way | The Cloud-Native Way (CNPG) |
| Edit postgresql.conf | SSH + manually edit the text file. | Update spec.postgresql.parameters in YAML. |
| Edit pg_hba.conf | Append host rules to the file on disk. | Update spec.postgresql.pg_hba list in YAML. |
| Apply Changes | Run pg_ctl reload or restart the service | Apply the YAML; CNPG determines if a reload or rolling restart is needed. |
Configuring Core Parameters Declaratively
To change any database parameter, you don't touch the container filesystem; you edit the cluster manifest. Performance tuning lives under spec.postgresql.parameters. For instance, a standard configuration for memory and connections might look like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
apiVersion: postgresql.cnpg.io/v1 kind: Cluster metadata: name: prod-postgres spec: instances: 3 postgresql: parameters: max_connections: "500" shared_buffers: "4GB" effective_cache_size: "12GB" work_mem: "32MB" maintenance_work_mem: "1GB" logging_collector: "on" log_min_messages: "warning" storage: size: 50Gi |
The operator’s reaction to this YAML is more than a simple file write. It begins a workflow of validation and execution:
- Generation: CNPG builds the actual
postgresql.confdynamically inside every pod. - Dynamic Reloads: For settings that can change at runtime, such as
work_mem, the operator applies them and triggers a live config reload. - Rolling Restarts: For parameters that are immutable at runtime—like
shared_buffersormax_connections—it orchestrates a rolling update. The operator restarts replicas sequentially, executes a clean switchover from the primary to an updated replica, and then cycles the old primary. The result is no data loss and negligible downtime.
Client Authentication: Safety First
One misstep in pg_hba.conf can lock you out of your own database—a severe risk when editing files on a live VM. CNPG removes that risk by generating and managing that file for you. It provisions the necessary internal rules for cluster connectivity (such as streaming replication) and pod-local access by default.
To grant access to external apps or network segments, you declare your additions in the spec.postgresql.pg_hba section:
|
1 2 3 4 5 6 7 8 |
spec: postgresql: pg_hba: # Allow local connections via local loopback - host all all 127.0.0.1/32 trust # Force SSL and password authentication for your application subnet - hostssl app_db app_user 10.244.0.0/16 md5 # Reject everything else (CNPG puts a safe default at the end of the file) |
Once applied, CNPG regenerates the pg_hba.conf and reloads the configuration safely, without dropping active sessions.
User Management Without Plaintext Credentials
Hardcoding passwords in manifest files is a non-starter for production security. CNPG accordingly links user authentication to Kubernetes Secrets rather than the manifest itself. You identify the users you need in your cluster spec, but the operator pulls their credentials from encrypted secret objects.
For example, this snippet bootstraps an application database with a dedicated user:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
apiVersion: postgresql.cnpg.io/v1 kind: Cluster metadata: name: prod-postgres spec: instances: 3 # Define the database and owner to be created during bootstrap bootstrap: initdb: database: app_db owner: app_user # CNPG will look for a Secret named 'app-user-credentials' # containing the 'password' key secret: name: app-user-credentials storage: size: 50Gi |
That setup requires a Secret created at the Kubernetes level:
|
1 2 3 |
kubectl create secret generic app-user-credentials \ --from-literal=username=app_user \ --from-literal=password=SuperSecretPassword123! |
CNPG then assigns the password from that Secret to app_user, provisions the app_db schema, and sets ownership constraints automatically.
The Benefits of Losing SSH
Surrendering shell access is a trade-off—but the exchange yields tangible operational wins:
- GitOps & Auditing: The entire cluster configuration is captured in Git. A change to
shared_buffersor a rule inpg_hba.confbecomes a reviewable commit, not a forgotten tweak on an unreachable host. - Automated Failover: If a node dies in a classic environment, you must manually rebuild the OS, reinstall Postgres, and reconfigure. In a CNPG setup, the operator detects the loss and schedules a new pod on a healthy node, applying your entire YAML specification automatically.
- Built-in Guardrails: Malformed configuration is flagged by the Kubernetes API before it ever reaches a live database. Syntax errors that might otherwise corrupt a cluster are rejected upfront.
CloudNativePG doesn’t strip DBA capabilities; it replaces file-based tweaks with deterministic, repeatable artifacts. Your Postgres cluster becomes reproducible and self-healing—no terminal required.



