Operations
Backup And Restores
Custom Secret
Monitoring
tpl
This guide demonstrates how to configure a PostgreSQL cluster on KubeBlocks with:
This combination provides comprehensive data protection with minimal recovery point objectives (RPO).
Point-In-Time Recovery (PITR) allows you to restore a database to a specific moment in time by combining full backups with continuous binlog/wal/archive log backups.
For details on restoring data from both full backups and continuous binlog backups, refer to the Restore From PITR guide.
Before proceeding, ensure the following:
kubectl create ns demo
namespace/demo created
Backup Repository Configured:
BackupRepo
BackupRepo
status is Ready
Cluster is Running:
Running
stateCheck the list of backup methods.
As introduced in the Create Full Backup guide, KubeBlocks PostgreSQL supports these backup methods:
Feature | Method | Description |
---|---|---|
Full Backup | pg-basebackup | Uses pg_basebackup , a PostgreSQL utility to create a base backup |
Full Backup | wal-g | Uses wal-g to create a full backup (requires WAL-G configuration) |
Continuous Backup | archive-wal | Uploads PostgreSQL Write-Ahead Logging (WAL) files periodically to the backup repository, usually paired with pg-basebackup |
Continuous Backup | wal-g-archive | Uploads PostgreSQL Write-Ahead Logging (WAL) files periodically to the backup repository, usually paired with wal-g |
pg-basebackup
with archive-wal
, and pair wal-g
with wal-g-archive
.pg-basebackup
can be enabled alone to create a full backup.wal-g
cannot be enabled alone. It must be paired with wal-g-archive
.In this guide, we will show how to deploy a PostgreSQL cluster with different combinations of backup methods. You may choose one of the following combinations.
Deploy a 2-node PostgreSQL replication cluster (1 primary, 1 secondary) and specify backup information.
apiVersion: apps.kubeblocks.io/v1
kind: Cluster
metadata:
name: pg-cluster
namespace: demo
spec:
terminationPolicy: Delete
clusterDef: postgresql
topology: replication
componentSpecs:
- name: postgresql
serviceVersion: 16.4.0
disableExporter: true
replicas: 2
resources:
limits:
cpu: "0.5"
memory: "0.5Gi"
requests:
cpu: "0.5"
memory: "0.5Gi"
volumeClaimTemplates:
- name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
backup:
retentionPeriod: 7d
# for full backup
method: pg-basebackup # full backup methnod name
enabled: true
cronExpression: 0 18 * * * # full backup scheuler
# for continuous backup
continuousMethod: archive-wal # continuous backup method
pitrEnabled: true # enable continous method or not
repoName: s3-repo # specify backuprepo, if not specified, the BackupRepo annotated as `default` will be used.
Or you can patch an existing cluster to enable scheduled continuous backup:
kubectl patch cluster pg-cluster -n demo --type='merge' -p='
{
"spec": {
"backup": {
"retentionPeriod": "7d",
"method": "pg-basebackup",
"enabled": true,
"cronExpression": "0 18 * * *",
"continuousMethod": "archive-wal",
"pitrEnabled": true,
"repoName": "s3-repo"
}
}
}'
Key Configuration Fields Explained
Field | Value | Description |
---|---|---|
backup.enabled | true | Enables scheduled backups |
method | pg-basebackup | Full backup method using PostgreSQL's native utility |
cronExpression | 0 18 * * * | Daily full backup at 6PM UTC |
retentionPeriod | 7d | Retains backups for 7 days |
repoName | s3-repo | Backup repository name (S3-compatible storage) |
pitrEnabled | true | Enables continuous WAL archiving for PITR |
continuousMethod | archive-wal | Method for continuous WAL archiving |
Monitor the cluster status until it transitions to the Running state:
kubectl get cluster pg-cluster -n demo -w
Example Output:
NAME CLUSTER-DEFINITION TERMINATION-POLICY STATUS AGE
pg-cluster postgresql Delete Creating 50s
pg-cluster postgresql Delete Running 4m2s
Once the cluster status becomes Running, your PostgreSQL cluster is ready for use.
Verify full backup and continuous backups are configured correctly:
Check full backup task is scheduled correctly. KubeBlocks create CronJob to schedule full backup.
kubectl get cronjob -l app.kubernetes.io/instance=pg-cluster,app.kubernetes.io/managed-by=kubeblocks-dataprotection -n demo
Check continuous backup is RUNNING correctly. KubeBlocks create a StatefulSet to run continuous backup.
kubectl get sts -l app.kubernetes.io/instance=pg-cluster,app.kubernetes.io/managed-by=kubeblocks-dataprotection -n demo
Check continuous Backup resource is created correctly.
kubectl get backup -l app.kubernetes.io/instance=pg-cluster,app.kubernetes.io/managed-by=kubeblocks-dataprotection,dataprotection.kubeblocks.io/backup-type=Continuous -n demo
Example Output:
NAME POLICY METHOD REPO STATUS TOTAL-SIZE DURATION DELETION-POLICY CREATION-TIME COMPLETION-TIME EXPIRATION-TIME
b387c27b-pg-cluster-postgresql-archive-wal pg-cluster-postgresql-backup-policy archive-wal <BACKUP_REPO> Running Delete 2025-05-16T02:58:10Z
KubeBlocks automatically creates a BackupSchedule
resource. Inspect the configuration:
kubectl get backupschedule pg-cluster-postgresql-backup-schedule -n demo -oyaml
Example Output:
apiVersion: dataprotection.kubeblocks.io/v1alpha1
kind: BackupSchedule
...
spec:
backupPolicyName: pg-cluster-postgresql-backup-policy
schedules:
- backupMethod: pg-basebackup
cronExpression: 0 18 * * *
enabled: true
retentionPeriod: 7d
- backupMethod: archive-wal
cronExpression: '*/5 * * * *'
enabled: true
name: archive-wal
retentionPeriod: 7d
Deploy a 2-node PostgreSQL replication cluster (1 primary, 1 secondary)
apiVersion: apps.kubeblocks.io/v1
kind: Cluster
metadata:
name: pg-cluster
namespace: demo
spec:
terminationPolicy: Delete
clusterDef: postgresql
topology: replication
componentSpecs:
- name: postgresql
serviceVersion: 16.4.0
disableExporter: true
replicas: 2
resources:
limits:
cpu: "0.5"
memory: "0.5Gi"
requests:
cpu: "0.5"
memory: "0.5Gi"
volumeClaimTemplates:
- name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
backup:
retentionPeriod: 7d
# for full backup
method: wal-g # full backup methnod name
enabled: true
cronExpression: 0 18 * * * # full backup scheuler
# for continuous backup
continuousMethod: wal-g-archive # continuous backup method
pitrEnabled: true # enable continous method or not
repoName: s3-repo # specify backuprepo, if not specified, the BackupRepo annotated as `default` will be used.
Or you can patch an existing cluster to enable scheduled continuous backup:
kubectl patch cluster pg-cluster -n demo --type='merge' -p='
{
"spec": {
"backup": {
"retentionPeriod": "7d",
"method": "wal-g",
"enabled": true,
"cronExpression": "0 18 * * *",
"continuousMethod": "wal-g-archive",
"pitrEnabled": true,
"repoName": "s3-repo"
}
}
}'
You may use the steps introduced in the Deploy a PostgreSQL Cluster, with wal-g and wal-g-archive section to check the deployment of the cluster.
Apart from that, you need to check if archive_command
is configured correctly.
Method wal-g
and wal-g-archive
work only when PostgreSQL's archive_command
is configured properly.
Kubeblocks will do it automatically for you when method wal-g-archive
is enabled.
KubeBlocks will create a Reconfiguration OpsRequest to set the archive_command
to envdir /home/postgres/pgdata/wal-g/env /home/postgres/pgdata/wal-g/wal-g wal-push %p
To monitor the progress of the reconfiguration, you can check the status of the OpsRequest:
kubectl get opsrequest -l app.kubernetes.io/instance=pg-cluster,operations.kubeblocks.io/ops-type=Reconfiguring -n demo
If the OpsRequest is Failed
or is Pending
for quite a long time, you need to check the logs of the OpsRequest to see the error message. Otherwise the wal-g-archive and wal-g will not work.
Each continuous backup methods upload WAL files to the backup repository periodically (every few seconds).
If there is no wal log to upload, the backup pod has nothing to do. Then backup's total-size won't increase and backup's timeRange
won't change.
You may perform a sysbench benchmark against the cluster or you can run the python script pg_benchmark.py to benchmark the cluster.
Check the progress of continuous backup by following the steps below:
checking the backup resource.
kubectl get backup -l app.kubernetes.io/instance=pg-cluster,dataprotection.kubeblocks.io/backup-type=Continuous -n demo
Example Output:
NAME POLICY METHOD REPO STATUS TOTAL-SIZE DURATION DELETION-POLICY CREATION-TIME COMPLETION-TIME EXPIRATION-TIME
b387c27b-pg-cluster-postgresql-archive-wal pg-cluster-postgresql-backup-policy archive-wal <BACKUP_REPO> Running 104325519 Delete 2025-05-16T02:58:10Z
TOTAL-SIZE
is the total size of the WAL files uploaded to the backup repository. It will increase over time.checking the backup pod logs.
kubectl logs b387c27b-pg-cluster-postgresql-archive-wal-0 -n demo -c backupdata
where b387c27b-pg-cluster-postgresql-archive-wal-0
is the name of the backup pod, and backupdata
is the container name.
Example Output:
2025-05-16 06:36:12 INFO: start to upload the wal log which maybe misses
2025-05-16 06:36:13 INFO: start to archive wal logs
2025-05-16 06:46:14 INFO: start to switch wal file
2025-05-16 06:56:16 INFO: start to switch wal file
2025-05-16 07:05:18 INFO: upload 000000010000000000000004
2025-05-16 07:05:20 INFO: upload 000000010000000000000005
2025-05-16 07:06:17 INFO: upload 000000010000000000000006
2025-05-16 07:09:44 INFO: upload 000000010000000000000007
2025-05-16 07:09:46 INFO: upload 000000010000000000000008
2025-05-16 07:09:48 INFO: upload 000000010000000000000009
...
kubectl logs b387c27b-pg-cluster-postgresql-wal-g-archive-0 -n demo -c backupdata
Where b387c27b-pg-cluster-postgresql-wal-g-archive-0
is the name of the backup pod, and backupdata
is the container name.
Example Output:
2025-05-16 08:16:59 INFO: start to archive and update wal infos
2025-05-16 08:16:59 INFO: config wal-g environment variables...
2025-05-16 08:25:18 INFO: upload 000000010000000000000022 ...
INFO: 2025/09/08 08:25:18.904842 Files will be uploaded to storage: default
INFO: 2025/09/08 08:25:22.729796 FILE PATH: 000000010000000000000023.zst
INFO: 2025/09/08 08:25:22.845932 FILE PATH: 000000010000000000000022.zst
2025-05-16 08:25:22 INFO: WAL-G upload succeeded for 000000010000000000000022
2025-05-16 08:25:22 INFO: upload 000000010000000000000023 ...
INFO: 2025/09/08 08:25:22.883391 Files will be uploaded to storage: default
2025-05-16 08:25:22 INFO: WAL-G upload succeeded for 000000010000000000000023
2025-05-16 08:25:24 INFO: start time of the oldest wal: 2025-05-16T08:09:45Z, end time of the latest wal: 2025-05-16T08:25:17Z, total size: 14938340
2025-05-16 08:26:23 INFO: upload 000000010000000000000024 ...
INFO: 2025/09/08 08:26:23.166320 Files will be uploaded to storage: default
INFO: 2025/09/08 08:26:25.738435 FILE PATH: 000000010000000000000025.zst
INFO: 2025/09/08 08:26:25.784766 FILE PATH: 000000010000000000000024.zst
2025-05-16 08:26:25 INFO: WAL-G upload succeeded for 000000010000000000000024
2025-05-16 08:26:25 INFO: upload 000000010000000000000025 ...
INFO: 2025/09/08 08:26:25.822544 Files will be uploaded to storage: default
2025-05-16 08:26:25 INFO: WAL-G upload succeeded for 000000010000000000000025
2025-05-16 08:26:27 INFO: start time of the oldest wal: 2025-05-16T08:09:45Z, end time of the latest wal: 2025-05-16T08:26:20Z, total size: 29775523
checking the backup's timeRange
, it should increase over time.
kubectl get backup b387c27b-pg-cluster-postgresql-archive-wal -n demo -oyaml | yq '.status.timeRange'
where b387c27b-pg-cluster-postgresql-archive-wal
is the name of the continuous backup.
Example Output:
timeRange:
end: "2025-05-16T07:09:48Z"
start: "2025-05-16T06:36:12Z"
timeRange.start
is the start time of the backup (earliest PostgreSQL transaction time).timeRange.end
is the end time of the backup (latest PostgreSQL transaction time).This guide covered:
Key Benefits: