# Plane CE PostgreSQL backup CronJob — DRAFT (2026-09-03), NOT applied. # ns: plane (synapse K3s single node). Output: hostPath /var/backups/plane (root disk, auto-created). # # Scope decision (2026-09-03, practical): DB-only. MinIO dropped — uploads bucket # measured at 264 KB / 444 KB total; attachments are acceptable loss for this # personal 1-user instance (310 issues / 88 MB DB). Revisit only if usage grows. # # Credentials: read from the CURRENT chart-generated Secret (works today). After the # optional external-secrets migration (docs/plane-hardening/README.md Phase A) switch # the secretKeyRef name to plane-pgdb-credentials. # # Apply: # ssh windy@synapse.chans.xyz 'sudo k3s kubectl apply -n plane -f -' < plane-backup.yaml # Manual run + verify: # sudo k3s kubectl -n plane create job --from=cronjob/plane-backup plane-backup-manual-1 # sudo k3s kubectl -n plane get cronjob,job,pods | grep plane-backup # sudo ls -lh /var/backups/plane/pg # Restore steps + tuning: see backup/README.md apiVersion: batch/v1 kind: CronJob metadata: name: plane-backup namespace: plane spec: # 01:30 UTC daily = 03:30 local (CEST). CronJob controller runs in UTC. schedule: "30 1 * * *" concurrencyPolicy: Forbid successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 2 jobTemplate: spec: backoffLimit: 2 template: spec: restartPolicy: OnFailure volumes: - name: backup hostPath: path: /var/backups/plane type: DirectoryOrCreate containers: - name: pg-dump image: postgres:15.7-alpine env: - name: PGPASSWORD valueFrom: secretKeyRef: name: plane-app-pgdb-secrets # -> plane-pgdb-credentials after Phase A key: POSTGRES_PASSWORD command: ["/bin/sh", "-c"] args: - | set -euo pipefail TS=$(date -u +%Y%m%dT%H%M%SZ) mkdir -p /backup/pg pg_dump -h plane-app-pgdb.plane.svc.cluster.local -U plane -d plane \ -Fc -f "/backup/pg/plane-${TS}.dump" find /backup/pg -type f -name 'plane-*.dump' -mtime +7 -delete echo "pg_dump done: /backup/pg/plane-${TS}.dump ($(du -h /backup/pg/plane-${TS}.dump | cut -f1))" volumeMounts: - name: backup mountPath: /backup