Skip to main content
Version: 8.0.2 (latest)

Logical PostgreSQL Backup

If it hasn't been done already, the bitnami Helm repository needs to be added to your local configuration:

# Add bitnami helm repo
$ helm repo add bitnami https://charts.bitnami.com/bitnami

Install the PostgreSQL chart from the bitnami Helm repository:

$ kubectl create namespace postgresql
$ helm install --namespace postgresql postgres bitnami/postgresql

Next create a file postgres-blueprint.yaml with the following contents

apiVersion: cr.kanister.io/v1alpha1
kind: Blueprint
metadata:
name: postgres-bp
actions:
backup:
kind: StatefulSet
outputArtifacts:
pgBackup:
# Capture the kopia snapshot information for subsequent actions
# The information includes the kopia snapshot ID which is essential for restore and delete to succeed
# `kopiaOutput` is the name provided to kando using `--output-name` flag
kopiaSnapshot: "{{ .Phases.pgDump.Output.kopiaOutput }}"
phases:
- func: MultiContainerRun
name: pgDump
objects:
pgSecret:
kind: Secret
name: '{{ index .Object.metadata.labels "app.kubernetes.io/instance" }}-postgresql'
namespace: '{{ .StatefulSet.Namespace }}'
args:
namespace: '{{ .StatefulSet.Namespace }}'
sharedVolumeMedium: Memory

initImage: '{{if index .Options "kanisterImage" }} {{- .Options.kanisterImage -}} {{else -}} ghcr.io/kanisterio/kanister-tools:0.113.0 {{- end}}'
initCommand: ["bash", "-o", "errexit", "-o", "pipefail", "-c", "mkfifo /tmp/data; chmod 666 /tmp/data"]

backgroundImage: postgres:17-bullseye
backgroundCommand:
- bash
- -o
- errexit
- -o
- pipefail
- -c
- |
export PGHOST='{{ index .Object.metadata.labels "app.kubernetes.io/instance" }}-postgresql.{{ .StatefulSet.Namespace }}.svc.cluster.local'
export PGUSER='postgres'
export PGPASSWORD='{{ index .Phases.pgDump.Secrets.pgSecret.Data "postgres-password" | toString }}'
pg_dumpall --clean -U $PGUSER > /tmp/data

outputImage: '{{if index .Options "kanisterImage" }} {{- .Options.kanisterImage -}} {{else -}} ghcr.io/kanisterio/kanister-tools:0.113.0 {{- end}}'
outputCommand:
- bash
- -o
- errexit
- -o
- pipefail
- -c
- |
backup_file_path="backup.sql"
cat /tmp/data | kando location push --profile '{{ toJson .Profile }}' --path "${backup_file_path}" --output-name "kopiaOutput" -

restore:
kind: StatefulSet
inputArtifactNames:
# The kopia snapshot info created in backup phase can be used here
# Use the `--kopia-snapshot` flag in kando to pass in `pgBackup.KopiaSnapshot`
- pgBackup
phases:
- func: MultiContainerRun
name: pgRestore
objects:
pgSecret:
kind: Secret
name: '{{ index .Object.metadata.labels "app.kubernetes.io/instance" }}-postgresql'
namespace: '{{ .StatefulSet.Namespace }}'
args:
namespace: '{{ .StatefulSet.Namespace }}'
sharedVolumeMedium: Memory

initImage: '{{if index .Options "kanisterImage" }} {{- .Options.kanisterImage -}} {{else -}} ghcr.io/kanisterio/kanister-tools:0.113.0 {{- end}}'
initCommand: ["bash", "-o", "errexit", "-o", "pipefail", "-c", "mkfifo /tmp/data; chmod 666 /tmp/data"]

backgroundImage: '{{if index .Options "kanisterImage" }} {{- .Options.kanisterImage -}} {{else -}} ghcr.io/kanisterio/kanister-tools:0.113.0 {{- end}}'
backgroundCommand:
- bash
- -o
- errexit
- -o
- pipefail
- -c
- |
backup_file_path="backup.sql"
kopia_snap='{{ .ArtifactsIn.pgBackup.KopiaSnapshot }}'
kando location pull --profile '{{ toJson .Profile }}' --path "${backup_file_path}" --kopia-snapshot "${kopia_snap}" - > /tmp/data

outputImage: postgres:17-bullseye
outputCommand:
- bash
- -o
- errexit
- -o
- pipefail
- -c
- |
export PGHOST='{{ index .Object.metadata.labels "app.kubernetes.io/instance" }}-postgresql.{{ .StatefulSet.Namespace }}.svc.cluster.local'
export PGUSER='postgres'
export PGPASSWORD='{{ index .Phases.pgRestore.Secrets.pgSecret.Data "postgres-password" | toString }}'
cat /tmp/data | psql -q -U "${PGUSER}"

delete:
inputArtifactNames:
# The kopia snapshot info created in backup phase can be used here
# Use the `--kopia-snapshot` flag in kando to pass in `pgBackup.KopiaSnapshot`
- pgBackup
phases:
- func: KubeTask
name: deleteDump
args:
image: '{{if index .Options "kanisterImage" }} {{- .Options.kanisterImage -}} {{else -}} ghcr.io/kanisterio/kanister-tools:0.113.0 {{- end}}'
namespace: "{{ .Namespace.Name }}"
command:
- bash
- -o
- errexit
- -o
- pipefail
- -c
- |
backup_file_path="backup.sql"
kopia_snap='{{ .ArtifactsIn.pgBackup.KopiaSnapshot }}'
kando location delete --profile '{{ toJson .Profile }}' --path "${backup_file_path}" --kopia-snapshot "${kopia_snap}"

And then apply the file using:

$ kubectl --namespace kasten-io apply -f postgres-blueprint.yaml

For PostgreSQL App Versions 14.x or older, Kanister tools version 0.85.0 is required.

$ kubectl --namespace kasten-io apply -f \
https://raw.githubusercontent.com/kanisterio/kanister/0.85.0/examples/postgresql/blueprint-v2/postgres-blueprint.yaml
Note

The PostgreSQL backup example provided above serve as a blueprint template for logical backups. Please note that these examples may need to be modified for specific production environments and setups. As a result, it is highly recommended to carefully review and modify the blueprints as needed before deploying them for production use.

Alternatively, use the Blueprints page on Veeam Kasten Dashboard to create the Blueprint resource.

Once the Blueprint is created, add an annotation on the PostgreSQL Deployment to instruct Veeam Kasten to use the Blueprint when performing operations on this PostgreSQL instance.

$ kubectl --namespace postgresql annotate statefulset/postgres-postgresql \
kanister.kasten.io/blueprint=postgres-bp

Finally, use Veeam Kasten to backup and restore the application.