Skip to content

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 30-day window. 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); talon-pg has its own (talon-pg-backups). Sharing a bucket is incidental, not a platform rule.

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

CNPG namespaces backups under a bucket by cluster name, so one bucket safely holds several clusters’ folders side by side.

Identical on both clusters apart from the credential Secret. From workloads/penvoice/base/postgres-cluster.yaml (overlay patches the prefix per env):

backup:
retentionPolicy: "30d"
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: gzip

keycloak-pg uses the same endpoint and keycloak-pg-backup-creds; overlays set …/keycloak-staging/ or …/keycloak-prod/. WAL and base data are gzip; retention is 30 days.

Each cluster has a ScheduledBackup. CNPG cron is 6-field (seconds first):

ClusterScheduledBackupScheduleTime
penvoice-pgpenvoice-pg-daily0 0 2 * * *02:00 daily
keycloak-pgkeycloak-pg-daily0 30 2 * * *02:30 daily
talon-pgtalon-pg-daily0 0 3 * * *03:00 daily
infisical-pginfisical-pg-daily0 0 4 * * *04:00 daily
spec:
schedule: "0 0 2 * * *"
backupOwnerReference: self
cluster:
name: penvoice-pg

A nightly base backup + continuous WAL archiving together provide PITR over the 30-day retention window.

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-1
  • AWS_*_CHECKSUM_CALCULATION=when_required — botocore ≥ 1.36 sends checksum trailers that Oracle returns NotImplemented for; 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. :::