Skip to content

Cloud Native PostgreSQL (Legacy)

The following text is a description of the old way of creating a database cluster and backups with CloudNative PostgreSQL. We strongly recommend you use the newer method described in postgresql.md, but provide the docs for the legacy method for now.

Example Cluster (Legacy)

Create the S3 Bucket

Create a Bucket in an Exoscale zone that is NOT at-vie-1 by following the instructions:

apiVersion: appcat.vshn.io/v1
kind: ObjectBucket
metadata:
   name: <project>-s3-bucket-pgbackup
   namespace: aris-<project>
spec:
   parameters:
     bucketName: <project>-s3-bucket-pgbackup
     region: at-vie-2
   writeConnectionSecretToRef:
     name: <project>-s3-bucket-pgbackup

This creates a bucket in Exoscale region at-vie-2 and writes the connection information to a secret, that can then be used by PostgreSQL.

Create the Database

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  namespace: aris-<project>
  name: <project>-db

spec:
  description: "Postgres cluster for <project>"
  imageName: ghcr.io/cloudnative-pg/postgresql:17.5
  instances: 3
  primaryUpdateStrategy: unsupervised
  startDelay: 300
  stopDelay: 300
  monitoring:
    enablePodMonitor: true

  # Persistent storage configuration
  storage:
    storageClass: rbd-storagepool-cluster
    size: 2Gi

  postgresql:
    parameters:
      max_slot_wal_keep_size: "1GB"

  bootstrap:
    initdb:
       database: xxx
       owner: xxx

  resources:
    requests:
      memory: "500Mi"
      cpu: "100m"  # should be 2
    limits:
      memory: "2Gi"

  affinity:
    enablePodAntiAffinity: true #default value
    topologyKey: kubernetes.io/hostname #default value
    podAntiAffinityType: required

  backup:
    barmanObjectStore:
      destinationPath: s3://<project>-s3-bucket-pgbackup/
      endpointURL: https://sos-at-vie-2.exo.io
      s3Credentials:
        accessKeyId:
          name: <project>-s3-bucket-pgbackup
          key: AWS_ACCESS_KEY_ID
        secretAccessKey:
          name: <project>-s3-bucket-pgbackup
          key: AWS_SECRET_ACCESS_KEY
      wal:
        compression: gzip
      data:
        compression: gzip
        jobs: 2
    retentionPolicy: "30d"

This will create 3 PostgreSQL Pods with their respective storage volumes. One of the Pods will be the primary database node, the others are followers.

Services for read-write and readonly connections are automatically created by the operator.

Backup the database

The YAML from above will create a database and backups of the write ahead logfiles (WAL). To restore the database from this backup you will need every WAL ever written.

We therefore recommend you create a backup schedule, e.g. backup your database by creating a ScheduledBackup custom resource:

apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
  name: <project>-db-scheduledbackup
  namespace: aris-<project>
spec:
  backupOwnerReference: self
  cluster:
    name: <project>-db
  method: barmanObjectStore
  schedule: 0 30 21 * * *
  target: primary

Use the database

You can find the information necessary to connect to your newly created database inside a secret created in your namespace.

Restore the database

So, you made a mistake, lost some data or want to create a copy of your database? Time for a restore from backup.

The following way of restoring a database is tried and tested. It creates a restore of the database to a new cluster, that can then be tested or used directly without touching the old database.

Step 1: Create a new S3 Bucket

See above. CloudnativePG refuses to use a Bucket, that is not completely empty, so create a new one. Use a different name for the Bucket, e.g. <project>-s3-bucket-pgbackup-new. This is actually not a very good name, but you know, naming things... As mentioned it is wise to create the Bucket in an Exoscale zone that is not at-vie-1.

Step 2: Create a new Database

The YAML manifest for creating a new database that restores automatically from a backup contains the same options for storage, PostgreSQL configuration, resource requests etc., but instead of starting the database with the initdb bootstrap stanza, we add the old backup S3 Bucket as a "External Cluster" source for the data and a new name. The following manifest is an abridged version of the code above with the relevant changes:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  namespace: aris-<project>
  name: <project>-db-new

spec:
[...]
  bootstrap:
    recovery:
      source: <project>-olddb
      recoveryTarget:
        targetTime: "2025-12-11 18:00:00.00000+02"

  externalClusters:
    - name: <project>-olddb
      barmanObjectStore:
        destinationPath: s3://<project>-s3-bucket-pgbackup/
        endpointURL: https://sos-at-vie-2.exo.io
        s3Credentials:
          accessKeyId:
            name: <project>-s3-bucket-pgbackup
            key: AWS_ACCESS_KEY_ID
          secretAccessKey:
            name: <project>-s3-bucket-pgbackup
            key: AWS_SECRET_ACCESS_KEY

[...]
  backup:
    barmanObjectStore:
      destinationPath: s3://<project>-s3-bucket-pgbackup-new/
      endpointURL: https://sos-at-vie-2.exo.io
      s3Credentials:
        accessKeyId:
          name: <project>-s3-bucket-pgbackup-new
          key: AWS_ACCESS_KEY_ID
        secretAccessKey:
          name: <project>-s3-bucket-pgbackup-new
          key: AWS_SECRET_ACCESS_KEY
      wal:
        compression: gzip
      data:
        compression: gzip
        jobs: 2
    retentionPolicy: "30d"

Step 3: Create a new Backup ScheduledBackups

See above, but adjust for the new database name and S3 Bucket.

The whole procedure will perform a restore of the old <project>-db database using the backups and WAL files stored in the Bucket <project>-s3-bucket-pgbackup. The new database will be called <project>-db-new and it will immediately start creating WAL backups and follow the schedule for creating ScheduledBackups.

Step 4: Use the restored database and clean up after yourself

Test your new database! Make sure it contains the data you want and only proceed if that is actually the case.

Depending on your use case, you might want to delete the old one. If you do so, don't forget to also delete the ScheduledBackup for the old database, as this one now has no target any more.

You can now also delete the old S3 Bucket.