Example: Postgres backups to Oracle
The generic recipe is in Onboard an app; this page shows it filled in for the Postgres clusters running on Project Loom.
Every on-cluster Postgres database backs up to Oracle Cloud Object Storage via
CloudNativePG’s barmanObjectStore. Continuous
WAL archiving plus a nightly base backup gives
point-in-time recovery across a 3-day window on staging and a 7-day window on prod (and
Infisical). Not every cluster shares a bucket. penvoice-pg,
keycloak-pg (per env prefix), and infisical-pg write to the same bucket (penvoice-pg-backups, into per-cluster
folders. Infisical is …/infisical/ on the Cluster, no overlay); talon-pg has its own (talon-pg-backups). Sharing
a bucket is incidental, not a platform rule.
Backup flow
Section titled “Backup flow”flowchart LR PA[penvoice-pg] -->|WAL + base| B[(s3://penvoice-pg-backups)] KC[keycloak-pg] -->|WAL + base| B INF[infisical-pg] -->|WAL + base| B TA[talon-pg] -->|WAL + base| B2[(s3://talon-pg-backups)] SB1[ScheduledBackup 02:00] -.triggers.-> PA SB2[ScheduledBackup 02:30] -.triggers.-> KC SB3[ScheduledBackup 04:00] -.triggers.-> INF SB4[ScheduledBackup 03:00] -.triggers.-> TA B --> O[Oracle Object Storage af-johannesburg-1] B2 --> O
Isolation is the destinationPath prefix, not the cluster name. Infisical writing at the bucket root fails barman’s
empty-archive check against objects from the other clusters.
The barmanObjectStore config
Section titled “The barmanObjectStore config”Identical on both clusters apart from the credential Secret. From
workloads/penvoice/base/postgres-cluster.yaml (overlay patches the prefix per env):
backup: retentionPolicy: "7d" barmanObjectStore: destinationPath: "s3://penvoice-pg-backups/" endpointURL: "https://axtbyjmrm2rt.compat.objectstorage.af-johannesburg-1.oraclecloud.com" s3Credentials: accessKeyId: name: penvoice-pg-backup-creds key: ACCESS_KEY_ID secretAccessKey: name: penvoice-pg-backup-creds key: ACCESS_SECRET_KEY wal: compression: gzip data: compression: gzipkeycloak-pg uses the same endpoint and keycloak-pg-backup-creds; overlays set
…/keycloak-staging/ or …/keycloak-prod/. infisical-pg sets …/infisical/ on the Cluster itself (single instance,
no env overlay). WAL and base data are gzip; retention is 7 days on prod (and Infisical), 3 days on staging.
The scheduled backups
Section titled “The scheduled backups”Each cluster has a ScheduledBackup. CNPG cron is 6-field (seconds first):
| Cluster | ScheduledBackup | Schedule | Time |
|---|---|---|---|
penvoice-pg | penvoice-pg-daily | 0 0 2 * * * | 02:00 daily |
keycloak-pg | keycloak-pg-daily | 0 30 2 * * * | 02:30 daily |
talon-pg | talon-pg-daily | 0 0 3 * * * | 03:00 daily |
infisical-pg | infisical-pg-daily | 0 0 4 * * * | 04:00 daily |
spec: schedule: "0 0 2 * * *" backupOwnerReference: self cluster: name: penvoice-pgA nightly base backup + continuous WAL archiving together provide PITR over that cluster’s retention window.
The Oracle S3-compat workaround
Section titled “The Oracle S3-compat workaround”Oracle’s S3-compatible endpoint needed two environment tweaks on each CNPG cluster, set under spec.env:
env: - name: AWS_REQUEST_CHECKSUM_CALCULATION value: when_required - name: AWS_RESPONSE_CHECKSUM_VALIDATION value: when_required - name: AWS_DEFAULT_REGION value: af-johannesburg-1AWS_*_CHECKSUM_CALCULATION=when_required. Botocore ≥ 1.36 sends checksum trailers that Oracle returnsNotImplementedfor; this disables them.AWS_DEFAULT_REGION=af-johannesburg-1. Signs SigV4 with the bucket’s real region.
:::caution The Customer Secret Key matters: the access key is clean hex, but the secret key contains +/=.
Swapping them puts a / into the SigV4 Credential and breaks header parsing. The keys live in out-of-band Secrets. One
per cluster, e.g. penvoice-pg-backup-creds, keycloak-pg-backup-creds, talon-pg-backup-creds,
infisical-pg-backup-creds (not in git, recreate on rebuild).
:::
:::note CNPG’s in-tree barmanObjectStore is deprecated (works through 1.29, removed in 1.30); the future move is the
Barman Cloud plugin. The platform runs CNPG operator v1.29.1.
:::