Automated Backups via Object Storage (Barman)
This guide is for platform teams configuring automated PostgreSQL backups to S3-compatible object storage and users who need to understand backup status on their PostgresCluster instances.
The Splunk Operator uses the barman-cloud CNPG plugin (barman-cloud.cloudnative-pg.io) as the backup method. This enables:
- Full base backups stored in object storage
- Continuous WAL archiving — every WAL segment is shipped to the store immediately, enabling Point-In-Time Recovery (PITR) to any moment between backups
- Retention policies — automatic pruning of old backups
For CNPG backup internals, see CloudNativePG — Barman Cloud Plugin.
Choosing a backup method: See Automated Backups via Volume Snapshots for the alternative CSI-based method. Both methods can coexist on the same cluster.
1. How it works (overview)
The operator:
- Creates a
barmancloud.cnpg.io/v1 ObjectStoreresource (named<cluster>-object-store) in the cluster’s namespace from the class configuration. Users do not create this manually. - Configures the CNPG
Clusterwith the barman-cloud plugin andisWALArchiver: true, enabling continuous WAL archiving immediately when the cluster starts. - Creates a CNPG
ScheduledBackupresource (methodplugin) to trigger periodic full base backups at the configured schedule.
CNPG then:
- Archives every WAL segment to the
ObjectStoredestination as it is produced. - At each scheduled time, takes a full base backup and uploads it to the store.
- Updates schedule metadata on the
ScheduledBackupstatus.
The Splunk Operator surfaces backup state on PostgresCluster.status.backupStatus.objectStore.
2. Prerequisites
- The barman-cloud CNPG plugin (
barmancloud.cnpg.io/v1 ObjectStoreCRD) must be installed on the cluster. It is not bundled with the Splunk Operator — install it separately via Helm if not already present:helm upgrade --install barman-cloud cnpg/plugin-barman-cloud \ --namespace cnpg-system --create-namespace - An S3-compatible bucket and credentials (access key or IAM role) with
s3:PutObject,s3:GetObject,s3:DeleteObject,s3:ListBucketon the bucket, stored as a Kubernetes Secret in the same namespace as thePostgresCluster.
3. Setup
Step 1 — Create the S3 credentials Secret
Users need to create one Secret in their namespace containing the AWS credentials. The operator handles everything else.
kubectl create secret generic s3-credentials -n <namespace> \
--from-literal=accessKeyId=<ACCESS_KEY_ID> \
--from-literal=secretAccessKey=<SECRET_ACCESS_KEY>
Step 2 — Configure the PostgresClusterClass (platform team)
The full S3 configuration lives in the class. The operator automatically creates a barmancloud.cnpg.io/v1 ObjectStore resource in each cluster’s namespace — users do not create it manually.
apiVersion: enterprise.splunk.com/v4
kind: PostgresClusterClass
metadata:
name: production
spec:
provisioner: postgresql.cnpg.io
config:
instances: 3
storage: 100Gi
postgresVersion: "17"
backup:
enabled: true
schedule: "0 2 * * *" # Daily at 2:00 AM (standard 5-field cron)
cnpg:
primaryUpdateMethod: switchover
backup:
# Which instance performs the base backup
target: prefer-standby # Avoids I/O on primary; falls back to primary if no standby
barmanObjectStore:
destinationPath: s3://my-pg-backups-bucket/clusters/
endpointURL: https://s3.us-east-1.amazonaws.com
retentionPolicy: "30d"
s3Credentials:
accessKeyId:
name: s3-credentials # Secret must exist in the cluster's namespace
key: accessKeyId
secretAccessKey:
name: s3-credentials
key: secretAccessKey
wal:
compression: gzip
Note: The
s3Credentialsfields reference a Secret by name only — the same Secret name is expected in every namespace where aPostgresClusterusing this class is deployed. The platform team controls the class; users create the Secret in their namespace.
Validation rules (enforced at admission):
config.backup.scheduleis required whenconfig.backup.enabledistrue.- At least one of
cnpg.backup.volumeSnapshotorcnpg.backup.barmanObjectStoreis required whenconfig.backup.enabledistrue.
Step 3 — Create the PostgresCluster (user / application team)
apiVersion: enterprise.splunk.com/v4
kind: PostgresCluster
metadata:
name: my-app-db
namespace: <namespace>
spec:
class: production
# Override: run backups at 3:30 AM instead of class default (2:00 AM)
backup:
schedule: "30 3 * * *"
If spec.backup is omitted, the cluster inherits both enabled and schedule from the class.
To disable backups on a specific cluster when the class has them enabled:
spec:
backup:
enabled: false
4. Configuration model
Object storage backup shares the same two-layer model as volume snapshots:
| Layer | Controls | Overridable by cluster? |
|---|---|---|
PostgresClusterClass.spec.config.backup | enabled, schedule | Yes |
PostgresClusterClass.spec.cnpg.backup | target, barmanObjectStore.* (destination, credentials, WAL, retention) | No (platform policy) |
PostgresCluster.spec.backup | enabled, schedule | N/A (this is the override) |
The operator creates and manages a barmancloud.cnpg.io/v1 ObjectStore resource (named <cluster>-object-store) in each cluster’s namespace from the class configuration. Users only need to create the credentials Secret referenced by s3Credentials.
5. Observing backup status
5.1 Conditions
The operator writes two conditions on every PostgresCluster:
ObjectStoreReady — tracks the managed ObjectStore resource:
| Status | Reason | Meaning |
|---|---|---|
True | ObjectStoreConfigured | ObjectStore created/updated successfully |
True | ObjectStoreDisabled | Barman not configured in the class |
False | ObjectStoreReconcileFailed | Error creating/updating the ObjectStore |
BackupReady — tracks the scheduled backup:
| Status | Reason | Meaning |
|---|---|---|
True | BackupConfigured | ScheduledBackup is active and healthy |
True | BackupDisabled | Backup is intentionally off |
False | BackupProviderMissing | backup.enabled=true but no provider configured in class |
False | ScheduledBackupFailed | Error creating/updating the ScheduledBackup |
False | ScheduledBackupCreated | Waiting for ScheduledBackup to appear (transient) |
5.2 BackupStatus
status:
backupStatus:
objectStore:
enabled: true
lastScheduleTime: "2026-04-30T02:00:00Z"
nextScheduleTime: "2026-05-01T02:00:00Z"
| Field | Description |
|---|---|
objectStore.enabled | Whether object store backups are active |
objectStore.lastScheduleTime | When CNPG last ran a full base backup (populated after first backup) |
objectStore.nextScheduleTime | Next scheduled base backup time |
Note: WAL archiving runs continuously regardless of the schedule — the schedule controls only full base backups. lastScheduleTime and nextScheduleTime are empty until the first scheduled base backup fires.
5.3 Verifying backups in S3
aws s3 ls s3://<bucket>/<prefix>/ --recursive | head -20
You should see both base/ (full backups) and wals/ (WAL segments):
2026-04-30T02:00:01Z my-cluster/base/20260430T020000/backup.info
2026-04-30T02:00:01Z my-cluster/base/20260430T020000/data.tar
2026-04-30T01:58:00Z my-cluster/wals/0000000100000000/000000010000000000000001
5.4 Verifying from within Kubernetes
# Check the managed ObjectStore resource
kubectl get objectstore <cluster-name>-object-store -n <namespace>
# List base backups taken by CNPG
kubectl get backups -n <namespace>
# Check the scheduled backup status (barman object-store backups)
kubectl get scheduledbackup <cluster-name>-backup-objectstore -n <namespace> -o yaml
Note on ScheduledBackup names. Each configured provider gets its own
ScheduledBackup: volume snapshots use<cluster-name>-backup(methodvolumeSnapshot) and barman object storage uses<cluster-name>-backup-objectstore(methodplugin). When both providers are configured they back up independently on the sharedconfig.backup.schedule; removing a provider garbage-collects itsScheduledBackup.
The ObjectStore status also shows the recovery window (first recoverability point and last successful backup) once backups have run:
kubectl get objectstore <cluster-name>-object-store -n <namespace> -o jsonpath='{.status}' | jq .
6. Retention
Retention is configured in the class via cnpg.backup.barmanObjectStore.retentionPolicy. The format is a number of days: "7d", "30d", "90d".
cnpg:
backup:
barmanObjectStore:
retentionPolicy: "30d"
The barman-cloud plugin automatically prunes base backups and WAL segments older than the policy after each new base backup is completed. The operator propagates this to the managed ObjectStore resource automatically.
7. Point-In-Time Recovery (PITR)
Because WAL is continuously archived, the backups in object storage can be replayed to any point in time between the first base backup and the last archived WAL segment.
This is driven entirely through the PostgresCluster API: create a new cluster with spec.bootstrapFrom.objectStorage (restore purely from the object-store base backup) or with spec.bootstrapFrom.volumeSnapshot.walArchive (a snapshot base plus WAL replay from object storage), optionally with a spec.bootstrapFrom.recoveryTarget. Bucket path and credentials are resolved from the class’s barmanObjectStore config — the same one used here for backups — so you can only restore from stores your class already has access to.
See Restoring a PostgresCluster §8 (PITR) and §9 (Restoring from object storage) for the full procedure and the recovery-target caveats.
8. Schedule format
See Automated Backups via Volume Snapshots — Schedule format. The same 5-field cron applies; the operator translates it to CNPG’s 6-field format internally.
9. Retention and cluster deletion
When clusterDeletionPolicy: Retain is set, the underlying CNPG Cluster is orphaned and continues running after the PostgresCluster CR is deleted. Both the ScheduledBackup and the ObjectStore CRs are operator-managed and owned by the PostgresCluster, so they are garbage-collected along with it. To avoid leaving the retained cluster with an active WAL archiver pointing at a soon-to-be-deleted ObjectStore, the operator strips the barman-cloud plugin from the retained CNPG Cluster spec during finalization (any plugins owned by other controllers are left intact).
Consequence: the retained CNPG Cluster will no longer take base backups, and WAL archiving stops cleanly when the PostgresCluster is deleted. This mirrors the volume-snapshot survivor, which keeps its (dormant) backup configuration but runs no active archiver. Existing base backups and WAL segments already written to object storage are not affected; they remain in the bucket and stay usable for PITR. The retained cluster therefore stops shipping new WAL to S3, so object-storage usage does not keep growing after deletion.
If ongoing backups and WAL archiving are required after retention, re-add the barman-cloud plugin to the retained CNPG Cluster and recreate the ObjectStore and ScheduledBackup CRs manually, targeting it with method: plugin.