feat(infra): nightly DB-dump CronJobs feeding the Restic backup
ci / typecheck (map[dir:apps/booking name:booking]) (push) Has been cancelled
ci / typecheck (map[dir:apps/portal name:portal]) (push) Has been cancelled
ci / typecheck (map[dir:apps/website name:website]) (push) Has been cancelled
ci / typecheck (map[dir:services/platform-api name:platform-api]) (push) Has been cancelled
ci / test (push) Has been cancelled

pg_dumpall (all Postgres DBs + roles) and mongodump (all Mongo DBs) write
gzipped dumps to the hostPath /opt/dezky-backup/dumps at 02:50/02:52 UTC, which
the host Restic job (03:20) ships to the Storage Box. Each keeps the last 7
local dumps; Restic holds the real off-box retention.

- pods run as root (hostPath dir is root-owned, as is the host Restic reader)
- mongo job uses bash (mongo:7 /bin/sh is dash → no pipefail)
- creds from postgres-secret / mongo-secret via secretKeyRef

Verified: both jobs Complete, dumps present on the host
(postgres-all ~2.2MB w/ Authentik data, mongo archive).
This commit is contained in:
Ronni Baslund
2026-06-08 21:55:14 +02:00
parent 861212831d
commit a27c238c76
2 changed files with 107 additions and 0 deletions
@@ -0,0 +1,106 @@
# Nightly logical DB dumps -> hostPath /opt/dezky-backup/dumps, where the host
# Restic job (03:20 UTC) picks them up and ships them to the Storage Box. These
# run at 02:50/02:52 UTC so the dumps are fresh when Restic runs. Each keeps the
# last 7 dumps locally (Restic keeps the real retention off-box).
#
# Pods run as root because the hostPath dir is root-owned (so the host Restic,
# also root, can read the dumps). Single-node cluster; trusted backup job.
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: postgres-backup
namespace: dezky-data
spec:
schedule: "50 2 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 1
template:
spec:
restartPolicy: Never
securityContext:
runAsUser: 0
containers:
- name: pgdump
image: postgres:16-alpine
command: ["/bin/sh", "-c"]
args:
- |
set -euo pipefail
ts=$(date +%Y%m%d-%H%M%S)
out=/dump/postgres-all-$ts.sql.gz
echo "pg_dumpall (all DBs + roles) -> $out"
PGPASSWORD="$POSTGRES_PASSWORD" pg_dumpall -h postgres.dezky-data -U postgres | gzip > "$out"
ls -1t /dump/postgres-all-*.sql.gz | tail -n +8 | xargs -r rm -f
ls -la /dump/postgres-all-*.sql.gz | tail -3
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: POSTGRES_PASSWORD
volumeMounts:
- name: dumps
mountPath: /dump
volumes:
- name: dumps
hostPath:
path: /opt/dezky-backup/dumps
type: DirectoryOrCreate
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: mongo-backup
namespace: dezky-data
spec:
schedule: "52 2 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 1
template:
spec:
restartPolicy: Never
securityContext:
runAsUser: 0
containers:
- name: mongodump
image: mongo:7
command: ["/bin/bash", "-c"]
args:
- |
set -euo pipefail
ts=$(date +%Y%m%d-%H%M%S)
out=/dump/mongo-$ts.archive.gz
echo "mongodump (all DBs) -> $out"
mongodump --host mongo.dezky-data \
--username "$MONGO_USER" --password "$MONGO_PASS" \
--authenticationDatabase admin --archive --gzip > "$out"
ls -1t /dump/mongo-*.archive.gz | tail -n +8 | xargs -r rm -f
ls -la /dump/mongo-*.archive.gz | tail -3
env:
- name: MONGO_USER
valueFrom:
secretKeyRef:
name: mongo-secret
key: root-username
- name: MONGO_PASS
valueFrom:
secretKeyRef:
name: mongo-secret
key: root-password
volumeMounts:
- name: dumps
mountPath: /dump
volumes:
- name: dumps
hostPath:
path: /opt/dezky-backup/dumps
type: DirectoryOrCreate
@@ -10,3 +10,4 @@ resources:
- postgres.yaml
- mongodb.yaml
- redis.yaml
- db-backup.yaml