--- # neuron-backup-credentials — restic + R2 + GCS for neuron prod DB backups apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: neuron-backup-credentials namespace: neuron-prod spec: refreshInterval: 1h secretStoreRef: name: vault kind: ClusterSecretStore target: name: neuron-backup-credentials creationPolicy: Owner template: data: # ── R2 (primary) ── RESTIC_PASSWORD: "{{ .restic_password }}" AWS_ACCESS_KEY_ID: "{{ .r2_access_key_id }}" AWS_SECRET_ACCESS_KEY: "{{ .r2_secret_access_key }}" RESTIC_REPOSITORY: "s3:https://651161e0a3d321561b4c90b5bcd5f15b.r2.cloudflarestorage.com/legion-neuron-backup" # ── GCS (secondary) ── RESTIC_PASSWORD_GCS: "{{ .restic_password_gcs }}" GCS_SA_KEY_JSON: "{{ .gcs_neuron_backup_sa_key }}" GOOGLE_PROJECT_ID: "neuron-785695" RESTIC_REPOSITORY_GCS: "gs:neuron-backup-neuron-785695:/" data: - secretKey: restic_password remoteRef: key: secret/data/r2 property: neuron_restic_password - secretKey: r2_access_key_id remoteRef: key: secret/data/r2 property: access_key_id - secretKey: r2_secret_access_key remoteRef: key: secret/data/r2 property: secret_access_key - secretKey: restic_password_gcs remoteRef: key: secret/data/gcs property: neuron_restic_password - secretKey: gcs_neuron_backup_sa_key remoteRef: key: secret/data/gcs property: neuron_backup_sa_key --- # Hourly neuron prod DB backup to Cloudflare R2 + Google Cloud Storage via restic. # Retention: 24 hourly, 7 daily, 4 weekly on both backends. # Mounts neuron-prod-data PVC read-only — safe on single-node k3s. apiVersion: batch/v1 kind: CronJob metadata: name: neuron-db-backup namespace: neuron-prod spec: schedule: "0 * * * *" concurrencyPolicy: Forbid failedJobsHistoryLimit: 3 successfulJobsHisticsLimit: 3 jobTemplate: spec: backoffLimit: 2 template: spec: restartPolicy: OnFailure containers: - name: backup image: restic/restic:latest command: - /bin/sh - -c - | set -e echo "=== Neuron DB backup: $(date -u) ===" # ── R2 backup (primary) ────────────────────────────────────────── echo "--- R2 backup ---" restic snapshots 2>/dev/null || restic init restic backup \ /data/neuron-prod.db \ --tag neuron-prod \ --host legion \ --exclude '*.db-shm' \ --exclude '*.db-wal' restic forget \ --keep-hourly 24 \ --keep-daily 7 \ --keep-weekly 4 \ --prune echo "R2 backup complete. Latest snapshots:" restic snapshots --latest 2 # ── GCS backup (secondary) ─────────────────────────────────────── echo "--- GCS backup ---" echo "$GCS_SA_KEY_JSON" > /tmp/gcs-sa.json ( export RESTIC_REPOSITORY="$RESTIC_REPOSITORY_GCS" export RESTIC_PASSWORD="$RESTIC_PASSWORD_GCS" export GOOGLE_APPLICATION_CREDENTIALS=/tmp/gcs-sa.json unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY restic snapshots 2>/dev/null || restic init restic backup \ /data/neuron-prod.db \ --tag neuron-prod \ --host legion \ --exclude '*.db-shm' \ --exclude '*.db-wal' restic forget \ --keep-hourly 24 \ --keep-daily 7 \ --keep-weekly 4 \ --prune echo "GCS backup complete. Latest snapshots:" restic snapshots --latest 2 ) rm -f /tmp/gcs-sa.json echo "=== All backups complete ===" envFrom: - secretRef: name: neuron-backup-credentials volumeMounts: - name: data mountPath: /data readOnly: true resources: requests: memory: 128Mi cpu: 50m limits: memory: 256Mi cpu: 200m volumes: - name: data persistentVolumeClaim: claimName: neuron-prod-data readOnly: true