Skip to content

5. Secrets & ObjectStore (R2)

Goal: create the database namespace, the credential Secrets, and the ObjectStore resource that tells the Barman Cloud Plugin where (and how) to store backups in Cloudflare R2. This is where the R2-specific workarounds live.

flowchart LR
    secret["Secret: r2-credentials<br/>(Access Key / Secret)"] --> os
    os["ObjectStore: pg-r2-store<br/>endpoint, region, checksum env"] --> r2[("R2 bucket")]
    cl["Cluster (next chapter)"] -->|barmanObjectName: pg-r2-store| os

Step 5.1 — Namespace

Keep the database in its own namespace, separate from applications.

kubectl create namespace production

Step 5.2 — Application credentials Secret

The Cluster will bootstrap an application database and user. Create its credentials (generate a strong password locally):

kubectl create secret generic pg-app-credentials \
  --from-literal=username=app_user \
  --from-literal=password="$(openssl rand -base64 24)" \
  -n production

Kubernetes Secrets are not encrypted by default

They are only base64-encoded at rest unless your cluster enables encryption at rest. For the production phase, consider the External Secrets Operator to keep secrets in a real vault. For learning, the above is fine.

Step 5.3 — R2 credentials Secret

In the Cloudflare dashboard, create an R2 API token scoped to your backup bucket. It yields an Access Key ID and Secret Access Key (S3-style). Also note your Account ID (used in the endpoint URL). Create the bucket first (recent barman-cloud does not auto-create buckets).

kubectl create secret generic r2-credentials \
  --from-literal=ACCESS_KEY_ID="<your-r2-access-key-id>" \
  --from-literal=ACCESS_SECRET_KEY="<your-r2-secret-access-key>" \
  -n production

Step 5.4 — The ObjectStore

This resource (provided by the Barman Cloud Plugin) defines the backup destination, compression, retention, and — critically for R2 — the workaround environment variables.

objectstore-r2.yaml
apiVersion: barmancloud.cnpg.io/v1
kind: ObjectStore
metadata:
  name: pg-r2-store
  namespace: production
spec:
  retentionPolicy: "30d"                 # (1)!
  instanceSidecarConfiguration:
    env:                                  # (2)!
      - name: AWS_DEFAULT_REGION
        value: "auto"                     # (3)!
      - name: AWS_REQUEST_CHECKSUM_CALCULATION
        value: "when_required"            # (4)!
      - name: AWS_RESPONSE_CHECKSUM_VALIDATION
        value: "when_required"
  configuration:
    destinationPath: "s3://<your-bucket>/pg-cluster/"     # (5)!
    endpointURL: "https://<account-id>.r2.cloudflarestorage.com"  # (6)!
    s3Credentials:
      accessKeyId:
        name: r2-credentials
        key: ACCESS_KEY_ID
      secretAccessKey:
        name: r2-credentials
        key: ACCESS_SECRET_KEY
    wal:
      compression: gzip
      maxParallel: 2
    data:
      compression: gzip
  1. Retention now lives on the ObjectStore (not the ScheduledBackup, as in the old in-tree model). Keep 30 days of backups+WAL.
  2. instanceSidecarConfiguration.env injects environment variables into the plugin sidecar that runs barman-cloud — this is how we apply R2 fixes.
  3. R2's region is auto. Setting AWS_DEFAULT_REGION via env is the reliable way; the s3Credentials.region field has been observed to have no effect.
  4. The checksum workaround. Recent AWS SDK data-integrity behavior triggers x-amz-content-sha256 errors on some S3-compatible stores. Forcing checksums to when_required avoids it.
  5. s3://<bucket>/<prefix>/ — the prefix groups this cluster's backups.
  6. The R2 S3 endpoint uses your Account ID.
kubectl apply -f objectstore-r2.yaml
kubectl get objectstore -n production

Step 5.5 — A first connectivity smoke test (optional but wise)

You will not see backups until the Cluster exists and runs one, but you can at least confirm the credentials and endpoint are plausible by listing the bucket with the AWS CLI from your laptop (R2 speaks S3):

AWS_ACCESS_KEY_ID=<id> AWS_SECRET_ACCESS_KEY=<secret> \
aws s3 ls --endpoint-url https://<account-id>.r2.cloudflarestorage.com \
  s3://<your-bucket>/

What could go wrong (R2-specific)

  • Bucket does not existbarman-cloud no longer creates it; create it first.
  • Missing checksum env vars → cryptic x-amz-content-sha256 upload errors.
  • Wrong endpoint/account ID → connection refused or auth errors.
  • Restore fails even though backups upload → this is the known R2 issue. Do not consider backups trustworthy until you complete the restore validation.

Where to go deeper

Next: The PostgreSQL Cluster — the centerpiece.