--- # neuron-backup-credentials — restic + R2 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: 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" 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 --- # Hourly neuron prod DB backup to Cloudflare R2 via restic. # Retention: 24 hourly, 7 daily, 4 weekly. # At current DB size (~10MB) this is <$0.01/month. # 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 successfulJobsHistoryLimit: 3 jobTemplate: spec: backoffLimit: 2 template: spec: restartPolicy: OnFailure containers: - name: backup image: restic/restic:latest command: - /bin/sh - -c - | set -e # Init repo on first run (no-op if already initialized) restic snapshots 2>/dev/null || restic init # Backup the SQLite DB file (WAL mode: exclude -shm/-wal, they're volatile) echo "Backing up neuron prod DB..." restic backup \ /data/neuron-prod.db \ --tag neuron-prod \ --host legion \ --exclude '*.db-shm' \ --exclude '*.db-wal' # Prune: 24 hourly, 7 daily, 4 weekly restic forget \ --keep-hourly 24 \ --keep-daily 7 \ --keep-weekly 4 \ --prune echo "Backup complete." restic snapshots --latest 3 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