PostgreSQL Backup and Restore
The Helm chart ships an optional scheduled backup CronJob that creates timestamped compressed PostgreSQL logical dumps (pg_dump custom format) and prunes old files according to a configurable retention policy.
Enabling backups
Set the following values (override in your values.yaml or via --set):
postgresql:
backup:
enabled: true
schedule: "0 2 * * *" # daily at 02:00 UTC (cron syntax)
retentionDays: 7 # delete dumps older than this many days
hostPath: /home/ioachim-minipc/news-dashboard-postgres-backups
Important:
hostPathmust be different frompostgresql.persistence.hostPath(the live data directory). The CronJob mounts the backup directory separately to prevent the retention cleanup from touching live data.
Apply with:
helm upgrade news-dashboard ./helm/news-dashboard \
--set postgresql.backup.enabled=true \
--set postgresql.backup.hostPath=/home/ioachim-minipc/news-dashboard-postgres-backups \
--reuse-values
Where dumps are stored
Dumps are written to the configured hostPath directory on the cluster node with
filenames of the form:
news_dashboard_20260628T020001Z.dump
Each dump uses pg_dump -Fc (custom binary format), which is compact, parallel-
restore capable, and safe for long-term storage.
Triggering a manual backup
You can run the backup job immediately without waiting for the schedule:
kubectl -n news-dashboard create job \
--from=cronjob/news-dashboard-news-dashboard-postgres-backup \
manual-backup-$(date +%Y%m%d)
Watch the job logs:
kubectl -n news-dashboard logs -l job-name=manual-backup-... -f
Restoring a dump
1. Copy the dump to a local machine (optional)
# From the node host, copy via scp or mount the backup directory directly.
scp ioachim-minipc:/home/ioachim-minipc/news-dashboard-postgres-backups/news_dashboard_20260628T020001Z.dump .
2. Restore into the running Helm PostgreSQL pod
# Find the postgres pod name:
POD=$(kubectl -n news-dashboard get pod -l app.kubernetes.io/name=news-dashboard-postgres -o jsonpath='{.items[0].metadata.name}')
# Copy the dump into the pod:
kubectl -n news-dashboard cp news_dashboard_20260628T020001Z.dump "${POD}:/tmp/restore.dump"
# Drop and recreate the target database, then restore:
kubectl -n news-dashboard exec -it "${POD}" -- bash -c "
psql -U news_dashboard -d postgres -c 'DROP DATABASE IF EXISTS news_dashboard;'
psql -U news_dashboard -d postgres -c 'CREATE DATABASE news_dashboard;'
pg_restore -U news_dashboard -d news_dashboard /tmp/restore.dump
"
3. Restore into a fresh Helm deployment
Deploy the chart without the application (or with replicas=0), then follow step 2. Once the restore is confirmed, scale the application back up:
kubectl -n news-dashboard scale deployment news-dashboard --replicas=1
Verifying a dump (without restoring to production)
# Inspect the dump table of contents:
pg_restore --list news_dashboard_20260628T020001Z.dump | head -40
# Restore into a throwaway local container:
docker run --rm -e POSTGRES_PASSWORD=test -p 5433:5432 -d --name pg-verify postgres:16-alpine
sleep 3
pg_restore -h 127.0.0.1 -p 5433 -U postgres -d postgres \
--create news_dashboard_20260628T020001Z.dump
docker stop pg-verify
Disabling backups
helm upgrade news-dashboard ./helm/news-dashboard \
--set postgresql.backup.enabled=false \
--reuse-values
This removes the CronJob from the cluster; existing dump files on the host are not deleted.