--- # Nightly backup — dumps Postgres + Gitea data to Cloudflare R2 via restic # Schedule: 2am daily. Retains 30 daily / 12 weekly / 6 monthly snapshots. apiVersion: batch/v1 kind: CronJob metadata: name: gitea-backup namespace: git spec: schedule: "0 2 * * *" concurrencyPolicy: Forbid failedJobsHistoryLimit: 3 successfulJobsHistoryLimit: 3 jobTemplate: spec: backoffLimit: 2 template: spec: restartPolicy: OnFailure initContainers: - name: pg-dump image: postgres:18-alpine command: - /bin/sh - -c - >- pg_dumpall -h postgres-postgresql.platform.svc.cluster.local -U postgres --no-role-passwords > /dump/all-databases.sql && echo 'DB dump complete, size:' $(du -sh /dump/all-databases.sql) envFrom: - secretRef: name: backup-credentials volumeMounts: - name: dump mountPath: /dump resources: requests: memory: 128Mi cpu: 100m limits: memory: 256Mi containers: - name: backup image: restic/restic:latest command: - /bin/sh - -c - | set -e # Init repo if first run restic snapshots 2>/dev/null || restic init # Back up Gitea data + full DB dump echo "Running restic backup..." restic backup \ /gitea-data \ /dump/all-databases.sql \ --tag legion \ --host legion # Prune old snapshots restic forget \ --keep-daily 30 \ --keep-weekly 12 \ --keep-monthly 6 \ --prune echo "Backup complete." restic snapshots --latest 3 envFrom: - secretRef: name: backup-credentials volumeMounts: - name: gitea-data mountPath: /gitea-data readOnly: true - name: dump mountPath: /dump resources: requests: memory: 256Mi cpu: 100m limits: memory: 512Mi volumes: - name: gitea-data persistentVolumeClaim: claimName: gitea-data readOnly: true - name: dump emptyDir: {}