add nightly Vault raft snapshot backup to R2
Snapshot via vault operator raft snapshot save using a least-privilege periodic token. Stored in legion-vault-backup R2 bucket via restic. Retains 30 daily / 12 weekly / 6 monthly snapshots. Runs at 3am, offset from the Gitea backup at 2am.
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
---
|
||||
# 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: {}
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
# vault-backup-credentials — R2 keys + restic password + Vault snapshot token
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: vault-backup-credentials
|
||||
namespace: vault
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: vault
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: vault-backup-credentials
|
||||
creationPolicy: Owner
|
||||
template:
|
||||
data:
|
||||
VAULT_SNAPSHOT_TOKEN: "{{ .snapshot_token }}"
|
||||
RESTIC_PASSWORD: "vault-restic-2026"
|
||||
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-vault-backup"
|
||||
data:
|
||||
- secretKey: snapshot_token
|
||||
remoteRef:
|
||||
key: secret/data/vault/snapshot-token
|
||||
property: token
|
||||
- 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
|
||||
Reference in New Issue
Block a user