--- # Keycloak's PostgreSQL — a CloudNativePG Cluster (CR of the CNPG CRDs installed by platform/cnpg). # CARRIED BY platform-config (dependsOn platform) so the postgresql.cnpg.io CRDs exist first (F-MON1). # # DR-critical (R-IDP-05): storage on the 3-replica `longhorn` StorageClass (ADR-0003) — the data # survives node loss. CNPG creates the keycloak-db-app Secret (keys username/password) that the # Keycloak CR consumes, plus the keycloak-db-rw/-ro/-r Services. Keycloak is stateless and rebuilds # from this DB + the git-managed realm import (docs/15). apiVersion: postgresql.cnpg.io/v1 kind: Cluster metadata: name: keycloak-db namespace: keycloak spec: # Single Postgres instance on 3-replica Longhorn is acceptable for the home scale (ADR-0014: # "single replica with DB on Longhorn + backups is acceptable"). Raise to 3 for Postgres-level HA # as a future hardening (prod) — the storage is already 3-way replicated by Longhorn. instances: 1 imageName: ghcr.io/cloudnative-pg/postgresql:16.4 storage: storageClass: longhorn size: 5Gi bootstrap: initdb: database: keycloak owner: keycloak # Emit Postgres metrics so Prometheus scrapes DB health (R-OBS-01). The CNPG operator creates the # PodMonitor at runtime (CRD already present from Phase-4 monitoring) — no kustomize CRD-ordering hazard. monitoring: enablePodMonitor: true resources: requests: cpu: 100m memory: 256Mi limits: memory: 512Mi # --- Off-cluster DR backup (R-IDP-05) — CNPG-native Barman → the on-Proxmox MinIO `cnpg` bucket --- # Continuous WAL archiving + periodic base backups give proper Postgres PITR (superior to a Velero # PVC snapshot for a live DB). destinationPath is the BUCKET ROOT; CNPG appends serverName (defaults # to the Cluster name), so objects land under s3://cnpg/keycloak-db/. Path-style HTTP MinIO endpoint. # Credentials: the scoped key from the in-namespace SOPS Secret keycloak-db-backup-credentials # (a Pod only reads same-ns Secrets), kept in sync with platform/backup/minio-credentials.sops.yaml. # endpointURL is ${S3_BACKUP_ENDPOINT} (cluster-vars, R-ENV-02; dev http://192.168.1.9:9000) — prod # MUST point at its own off-cluster target. Substituted by the `platform-config` Kustomization. backup: retentionPolicy: "30d" # keep 30 days of base backups + WAL (PITR window); prunes older barmanObjectStore: destinationPath: s3://cnpg endpointURL: ${S3_BACKUP_ENDPOINT} s3Credentials: accessKeyId: name: keycloak-db-backup-credentials key: ACCESS_KEY_ID secretAccessKey: name: keycloak-db-backup-credentials key: ACCESS_SECRET_KEY wal: compression: gzip maxParallel: 2 data: compression: gzip --- # Daily base backup of keycloak-db (WAL is archived continuously by the barmanObjectStore above). # CNPG cron is 6-field (sec min hour dom mon dow). immediate:true takes a first base backup as soon # as the schedule is created so the PITR chain has a starting point. Staggered 01:00 (nextcloud 02:00, # semaphore 04:00, Velero 03:00) to avoid concurrent base backups hammering MinIO. apiVersion: postgresql.cnpg.io/v1 kind: ScheduledBackup metadata: name: keycloak-db-daily namespace: keycloak spec: immediate: true schedule: "0 0 1 * * *" backupOwnerReference: self cluster: name: keycloak-db method: barmanObjectStore