Back up a cluster

Schedule Raft snapshots, choose storage and OpenBao identities, trigger a backup, and verify the result.

Updated 14 October 2026 · 4 min read

On this page

The operator creates a transient Job for each due or manually requested backup. The Job authenticates to OpenBao, streams a Raft snapshot to object storage, and records the result on status.backup. Snapshot bytes do not pass through the controller.

Before you begin

Prepare two independent identities:

  1. An OpenBao identity that can read sys/storage/raft/snapshot. Prefer a JWT role bound to the generated <cluster>-backup-serviceaccount; use tokenSecretRef only as a compatibility path.
  2. An object-storage identity. Use credentialsSecretRef, workload-identity metadata, or S3 roleArn.

When self-init OIDC bootstrap is enabled, an empty jwtAuthRole resolves to openbao-operator-backup and the operator creates the policy and role during initial bootstrap. Otherwise, configure jwtAuthRole or tokenSecretRef explicitly.

Hardened clusters also require explicit, port-scoped spec.network.egressRules and an explicit storage identity. See Configure network policy.

Configure an S3 backup

This is the preferred minimal shape when self-init OIDC bootstrap is enabled:

configure

Schedule an S3 snapshot

spec:
  backup:
    schedule: "0 3 * * *"
    target:
      provider: s3
      endpoint: https://s3.amazonaws.com
      bucket: openbao-backups
      region: us-east-1
      pathPrefix: clusters
      credentialsSecretRef:
        name: s3-backup-credentials
    retention:
      maxCount: 14
      maxAge: 336h

schedule is a five-field cron expression. The default provider is s3, but set it explicitly so intent remains clear.

The operator persists the first scheduled run in status.backup.nextScheduledBackup before it waits for that run. The deadline survives reconciliation and controller restarts. Check status.backup.lastBackupTime and status.backup.lastBackupName to verify that a scheduled backup succeeds.

ProviderRequired target fieldsSecret keys
S3endpoint, bucket; region defaults to us-east-1accessKeyId, secretAccessKey; optional sessionToken, region, caCert
GCSbucket; optional gcs.project and endpointcredentials.json
Azurebucket, azure.storageAccount; optional azure.container and endpointaccountKey or connectionString

For platform workload identity, set target.workloadIdentity.serviceAccountAnnotations and, when required, target.workloadIdentity.podLabels. For S3 web identity, target.roleArn also projects an STS token and configures the Job explicitly.

Use a static OpenBao token only when needed

If JWT auth is unavailable, create a same-namespace Secret whose token value can read the Raft snapshot endpoint, then reference it:

configure

Reference a backup token

spec:
  backup:
    tokenSecretRef:
      name: openbao-backup-token

jwtAuthRole takes precedence when both fields are present. Rotate and revoke a static token as a high-sensitivity credential.

Delegate configuration safely

The identity that changes the OpenBaoCluster needs:

  • get on each referenced Secret
  • usecloudidentities on the cluster when it sets roleArn or workload-identity metadata
  • usecustomexecutables on the cluster when it overrides spec.backup.image (usehelperimages remains a compatibility alias)

Admission rejects loopback and link-local storage endpoints. Hardened clusters also reject insecure TLS and ambient storage identity.

Trigger and verify a backup

The operator does not create a CronJob. Trigger an immediate backup by changing the one-shot annotation value:

apply

Request a manual backup

kubectl -n <namespace> annotate openbaocluster <name> \
  openbao.org/trigger-backup="$(date -u +%Y-%m-%dT%H:%M:%SZ)" --overwrite

The controller clears the annotation after accepting the request. It skips a duplicate request while another backup Job is active.

verify

Inspect backup readiness and outcome

kubectl -n <namespace> get openbaocluster <name> \
  -o jsonpath='{range .status.conditions[?(@.type=="BackupConfigurationReady")]}{.status}{" "}{.reason}{"\n"}{.message}{"\n"}{end}'
kubectl -n <namespace> get openbaocluster <name> -o jsonpath='{.status.backup}' | jq
kubectl -n <namespace> get jobs -l openbao.org/cluster=<name>

A successful run advances lastBackupTime, lastBackupName, lastBackupSize, and lastBackupDuration. Failure details are in lastFailureReason, lastFailureMessage, and lastFailureTime. Confirm the object independently in storage before relying on it.

If the snapshot stream fails during upload, the executor aborts the write, so the failed snapshot creates no object. After upload, it checks that the stored object size matches the number of bytes streamed and deletes the object if they differ. That deletion can fail on object-lock or write-only buckets, so check for leftover objects after a failed run.

Set retention and pre-upgrade snapshots

maxCount: 0 and an empty maxAge mean unlimited retention. The operator applies retention after a successful upload and never turns a retention error into a failed snapshot. Retention always keeps the newest snapshot and the snapshot recorded in status.backup.lastBackupName, even when they are older than maxAge. Snapshot object keys use the time the backup Job was created, so a backup delayed past its schedule is not treated as old.

Retention only manages scheduled and manual snapshots stored directly under the cluster prefix. It leaves pre-upgrade and blue-green phase snapshots, nested objects, and any other files in that prefix untouched. Remove those by hand or with storage lifecycle rules.

Enable spec.upgrade.preUpgradeSnapshot: true to require a snapshot before either upgrade strategy. Blue-green also accepts spec.upgrade.blueGreen.preUpgradeSnapshot, but the top-level field keeps one policy across strategies. The upgrade does not continue if its required snapshot fails.

A backup is complete only after you restore it into an isolated target and validate the recovered data.

Search the handbook

Try “install”, “threat model”, or “compatibility”.