# ============================================================================ # postgresql-1 logical backup — 6-hourly pg_dump to HDD5 (rides Restic) # ---------------------------------------------------------------------------- # The shared CNPG cluster `postgresql` (database-system, single instance) had no # backup. This CronJob does a LOGICAL backup every 6h: # - pg_dumpall --globals-only -> globals.sql (roles/grants; needed for restore) # - pg_dump -Fc per database -> .dump (custom format; selective restore) # written to hostPath /mnt/5_hdd/backup/postgresql/dumps// (HDD5, OFF # the DB's own HDD4 disk). The host backup flow (/opt/backup, backup-postgresql.sh) # then restic-backs-up that dir in its daily run (7d/4w/6m retention). Local # retention here = 3 days. RPO <=6h, no PITR (accepted). # # Auth: connects to postgresql-rw as the CNPG-managed superuser (secret # `postgres-superuser`, keys username/password). Requires enableSuperuserAccess: # true on the Cluster (set in postgresql-cluster.yaml). The superuser is the only # role that can read every DB + dump globals; per-app roles cannot. # # Freshness alert: kube_cronjob_status_last_successful_time (KSM) — see # mon-system/prometheus-rules.yaml (PostgresqlDumpStale, >12h). # ============================================================================ --- apiVersion: v1 kind: ConfigMap metadata: name: postgresql-backup-script namespace: database-system labels: app.kubernetes.io/name: postgresql-backup app.kubernetes.io/instance: postgresql-backup data: pg-backup.sh: | #!/usr/bin/env bash set -euo pipefail export PGHOST="${PGHOST:-postgresql-rw}" PGPORT="${PGPORT:-5432}" TS="$(date +%Y%m%d-%H%M%S)" OUT="/backup/dumps/${TS}" mkdir -p "${OUT}" echo "[pgbackup] ${TS} -> ${OUT}" echo "[pgbackup] globals (roles/grants)" pg_dumpall --globals-only > "${OUT}/globals.sql" mapfile -t DBS < <(psql -tAc \ "SELECT datname FROM pg_database WHERE NOT datistemplate AND datallowconn ORDER BY 1;") echo "[pgbackup] databases: ${DBS[*]}" for db in "${DBS[@]}"; do echo "[pgbackup] pg_dump -Fc ${db}" pg_dump -Fc -d "${db}" -f "${OUT}/${db}.dump" done touch "${OUT}/SUCCESS" echo "[pgbackup] pruning local dumps older than 3 days" find /backup/dumps -mindepth 1 -maxdepth 1 -type d -mtime +3 -exec rm -rf {} + || true echo "[pgbackup] done" ls -lh "${OUT}" --- apiVersion: batch/v1 kind: CronJob metadata: name: postgresql-backup namespace: database-system labels: app.kubernetes.io/name: postgresql-backup app.kubernetes.io/instance: postgresql-backup spec: schedule: "0 */6 * * *" timeZone: "Europe/Budapest" concurrencyPolicy: Forbid successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 3 startingDeadlineSeconds: 600 jobTemplate: metadata: labels: app.kubernetes.io/name: postgresql-backup app.kubernetes.io/instance: postgresql-backup spec: backoffLimit: 2 activeDeadlineSeconds: 1800 template: metadata: labels: app.kubernetes.io/name: postgresql-backup app.kubernetes.io/instance: postgresql-backup annotations: # postgres image is pinned to the CNPG cluster's version (X.Y) match-regex.version-checker.io/pgbackup: '^\d+\.\d+$' spec: enableServiceLinks: false restartPolicy: OnFailure securityContext: runAsNonRoot: true runAsUser: 26 runAsGroup: 26 fsGroup: 26 containers: - name: pgbackup # MUST match the CNPG cluster's Postgres version (pg_dump >= server). image: ghcr.io/cloudnative-pg/postgresql:17.2 imagePullPolicy: IfNotPresent command: ["/bin/bash", "/scripts/pg-backup.sh"] env: - name: PGHOST value: postgresql-rw - name: PGPORT value: "5432" - name: PGUSER valueFrom: secretKeyRef: name: postgresql-superuser key: username - name: PGPASSWORD valueFrom: secretKeyRef: name: postgresql-superuser key: password - name: HOME value: /tmp resources: requests: cpu: 100m memory: 128Mi limits: cpu: "1" memory: 512Mi securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL volumeMounts: - name: script mountPath: /scripts readOnly: true - name: backup mountPath: /backup - name: tmp mountPath: /tmp volumes: - name: script configMap: name: postgresql-backup-script defaultMode: 0755 - name: backup hostPath: path: /mnt/5_hdd/backup/postgresql type: Directory - name: tmp emptyDir: sizeLimit: 256Mi