media: add nightly config backup to Cloudflare R2 via restic
Backs up all service config PVCs (Jellyfin, Radarr, Sonarr, Prowlarr, qBittorrent, Bazarr, Seerr) at 3am daily to legion-media-backup R2 bucket. Media files are excluded — they are re-downloadable.
This commit is contained in:
@@ -0,0 +1,115 @@
|
|||||||
|
# Nightly media config backup — backs up all service config PVCs to Cloudflare R2 via restic
|
||||||
|
# NOTE: Does NOT back up actual media files (movies, TV, etc.) — those are re-downloadable.
|
||||||
|
# Only backs up app configs: Jellyfin, Radarr, Sonarr, Sonarr, Prowlarr, qBittorrent, Bazarr, Seerr.
|
||||||
|
# Schedule: 3am daily (offset from Gitea backup at 2am)
|
||||||
|
# Retains: 30 daily / 12 weekly / 6 monthly snapshots
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: media-backup
|
||||||
|
namespace: media
|
||||||
|
spec:
|
||||||
|
schedule: "0 3 * * *"
|
||||||
|
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 if first run
|
||||||
|
restic snapshots 2>/dev/null || restic init
|
||||||
|
|
||||||
|
echo "Backing up media service configs..."
|
||||||
|
restic backup \
|
||||||
|
/jellyfin-config \
|
||||||
|
/radarr-config \
|
||||||
|
/sonarr-config \
|
||||||
|
/prowlarr-config \
|
||||||
|
/qbittorrent-config \
|
||||||
|
/bazarr-config \
|
||||||
|
/overseerr-config \
|
||||||
|
--tag media \
|
||||||
|
--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: media-backup-credentials
|
||||||
|
volumeMounts:
|
||||||
|
- name: jellyfin-config
|
||||||
|
mountPath: /jellyfin-config
|
||||||
|
readOnly: true
|
||||||
|
- name: radarr-config
|
||||||
|
mountPath: /radarr-config
|
||||||
|
readOnly: true
|
||||||
|
- name: sonarr-config
|
||||||
|
mountPath: /sonarr-config
|
||||||
|
readOnly: true
|
||||||
|
- name: prowlarr-config
|
||||||
|
mountPath: /prowlarr-config
|
||||||
|
readOnly: true
|
||||||
|
- name: qbittorrent-config
|
||||||
|
mountPath: /qbittorrent-config
|
||||||
|
readOnly: true
|
||||||
|
- name: bazarr-config
|
||||||
|
mountPath: /bazarr-config
|
||||||
|
readOnly: true
|
||||||
|
- name: overseerr-config
|
||||||
|
mountPath: /overseerr-config
|
||||||
|
readOnly: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 256Mi
|
||||||
|
cpu: 100m
|
||||||
|
limits:
|
||||||
|
memory: 512Mi
|
||||||
|
volumes:
|
||||||
|
- name: jellyfin-config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: jellyfin-config
|
||||||
|
readOnly: true
|
||||||
|
- name: radarr-config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: radarr-config
|
||||||
|
readOnly: true
|
||||||
|
- name: sonarr-config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: sonarr-config
|
||||||
|
readOnly: true
|
||||||
|
- name: prowlarr-config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: prowlarr-config
|
||||||
|
readOnly: true
|
||||||
|
- name: qbittorrent-config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: qbittorrent-config
|
||||||
|
readOnly: true
|
||||||
|
- name: bazarr-config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: bazarr-config
|
||||||
|
readOnly: true
|
||||||
|
- name: overseerr-config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: overseerr-config
|
||||||
|
readOnly: true
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# media-backup-credentials — R2 + restic credentials for nightly media config backup
|
||||||
|
apiVersion: external-secrets.io/v1beta1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: media-backup-credentials
|
||||||
|
namespace: media
|
||||||
|
spec:
|
||||||
|
refreshInterval: 1h
|
||||||
|
secretStoreRef:
|
||||||
|
name: vault
|
||||||
|
kind: ClusterSecretStore
|
||||||
|
target:
|
||||||
|
name: media-backup-credentials
|
||||||
|
creationPolicy: Owner
|
||||||
|
template:
|
||||||
|
data:
|
||||||
|
RESTIC_PASSWORD: "media-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-media-backup"
|
||||||
|
data:
|
||||||
|
- 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