Skip to main content

CNPG cluster (component)

A Kustomize component that turns "this app needs Postgres" into a single line in the app's kustomization.yaml. Pulls in a CloudNative-PG Cluster resource pre-tuned for the homelab's conventions — Longhorn-encrypted PVCs, k8up-driven snapshots, and a per-app pg_dump backup command.

What it composes

Per app, the component lands a Cluster resource named <app>-db in the app's namespace, with:

  • One primary, one replica by default — enough for survival of a single-node outage; tunable per app via the patch.
  • Longhorn-encrypted PVC for the data directory (sized via the app's patch).
  • k8up.io/backupcommand: pg_dump annotation so k8up snapshots a logical dump rather than the filesystem.
  • k8up.io/file-extension: .sql so the snapshot is restorable with plain psql.
  • Sensible defaults for postgresql.parameters (shared_buffers, work_mem, etc.) tuned for small homelab workloads.

The CNPG operator itself lives in platform/cloudnative-pg; this component is the per-app instance of its Cluster CRD.

How an app uses it

# k8s/apps/talos/<app>/kustomization.yaml
components:
- ../../../components/talos/cnpg
patches:
- target:
kind: Cluster
name: app-db
patch: |
- op: replace
path: /spec/storage/size
value: 20Gi

The app overrides whatever defaults don't fit — usually storage size and replica count. Anything not overridden takes the component's value.

Why pre-tune in a component, not per-app

  • Consistency. Every Postgres in the homelab is shaped the same way; backups, encryption, and replica counts don't drift app by app.
  • One place to update conventions. When a new CNPG version changes its Cluster API, the component absorbs the change once.
  • Backup wiring lives with the database, not the app. The k8up.io annotations are part of the component, so an app that uses the component automatically gets the right pg_dump snapshot path.

Operational notes

  • Restoring is the postgres-restore runbookkubectl exec into the primary and stream the dump through pv. The path is identical regardless of which app's database is being restored, thanks to the component's uniform shape.
  • An app that needs no Postgres just doesn't include this component. There's no global default — opt-in only.
  • Removing the component from an app's kustomization.yaml deletes the Cluster resource on next reconciliation — which deletes the PVC, which destroys the database. Restore-from-snapshot is the only recovery; warm tier covers it but RPO is one backup cycle.

Cluster Deployment

Depends on