--- # Nightly Vault backup — raft snapshot → Cloudflare R2 via restic # Schedule: 3am daily (offset from Gitea backup at 2am). # Retains 30 daily / 12 weekly / 6 monthly snapshots. apiVersion: batch/v1 kind: CronJob metadata: name: vault-backup namespace: vault spec: schedule: "0 3 * * *" concurrencyPolicy: Forbid failedJobsHistoryLimit: 3 successfulJobsHistoryLimit: 3 jobTemplate: spec: backoffLimit: 2 template: spec: restartPolicy: OnFailure serviceAccountName: default initContainers: - name: vault-snapshot image: hashicorp/vault:latest command: - /bin/sh - -c - | set -e echo "Taking Vault raft snapshot..." VAULT_TOKEN=$VAULT_SNAPSHOT_TOKEN \ vault operator raft snapshot save /snapshot/vault.snap echo "Snapshot complete, size: $(du -sh /snapshot/vault.snap)" env: - name: VAULT_ADDR value: "http://vault.vault.svc.cluster.local:8200" - name: VAULT_SNAPSHOT_TOKEN valueFrom: secretKeyRef: name: vault-backup-credentials key: VAULT_SNAPSHOT_TOKEN volumeMounts: - name: snapshot mountPath: /snapshot resources: requests: memory: 64Mi cpu: 50m 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 echo "Running restic backup..." restic backup \ /snapshot/vault.snap \ --tag vault \ --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: vault-backup-credentials volumeMounts: - name: snapshot mountPath: /snapshot readOnly: true resources: requests: memory: 128Mi cpu: 100m limits: memory: 256Mi volumes: - name: snapshot emptyDir: {}