From 066dc41b558b28bf29e329753a53030d00655324 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 15 Aug 2026 12:34:25 +1000 Subject: [PATCH] jellyfin: scope k8up backup to config PVC only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The jellyfin-config Schedule had no PVC selector and the k8up chart runs skipWithoutAnnotation=false, so restic swept in every PVC in the namespace (media, transcode scratch, redis, per-pod cache, CNPG data) — RWO volumes also fail to mount while in use. k8up 4.10.0's Schedule CRD has no positive PVC selector, so exclude every non-config PVC by annotation. - Annotate media-tv, media-movies, transcode and redis-data PVCs with k8up.io/backup: "false". - Annotate the per-pod cache volumeClaimTemplate the same way. - Propagate the annotation to the CNPG data PVCs via inheritedMetadata (postgres has its own barmanObjectStore backup). - Leave jellyfin-config unannotated so it remains the only backup target. --- apps/base/jellyfin/cnpg_cluster.yaml | 7 +++++++ apps/base/jellyfin/pvc-media-movies.yaml | 5 +++++ apps/base/jellyfin/pvc-media-tv.yaml | 5 +++++ apps/base/jellyfin/pvc-transcode.yaml | 5 +++++ apps/base/jellyfin/redis-pvc.yaml | 5 +++++ apps/base/jellyfin/statefulset.yaml | 5 +++++ 6 files changed, 32 insertions(+) diff --git a/apps/base/jellyfin/cnpg_cluster.yaml b/apps/base/jellyfin/cnpg_cluster.yaml index 9acd6c4..46a2476 100644 --- a/apps/base/jellyfin/cnpg_cluster.yaml +++ b/apps/base/jellyfin/cnpg_cluster.yaml @@ -10,6 +10,13 @@ metadata: name: jellyfin-postgres namespace: jellyfin spec: + # Exclude the operator-managed data PVCs (jellyfin-postgres-N) from the + # jellyfin-config k8up Schedule (skipWithoutAnnotation is false cluster-wide, + # so unannotated PVCs are swept in). Postgres has its own barmanObjectStore + # backup below; restic must not touch the raw RWO data volumes. + inheritedMetadata: + annotations: + k8up.io/backup: "false" affinity: podAntiAffinityType: preferred backup: diff --git a/apps/base/jellyfin/pvc-media-movies.yaml b/apps/base/jellyfin/pvc-media-movies.yaml index 4665bb1..5611832 100644 --- a/apps/base/jellyfin/pvc-media-movies.yaml +++ b/apps/base/jellyfin/pvc-media-movies.yaml @@ -7,6 +7,11 @@ kind: PersistentVolumeClaim metadata: name: jellyfin-media-movies namespace: jellyfin + annotations: + # Exclude from the jellyfin-config k8up Schedule (skipWithoutAnnotation is + # false cluster-wide, so unannotated PVCs are swept in). Only jellyfin-config + # is backed up; the media library is not restic-backup material. + k8up.io/backup: "false" spec: accessModes: - ReadWriteMany diff --git a/apps/base/jellyfin/pvc-media-tv.yaml b/apps/base/jellyfin/pvc-media-tv.yaml index 6780291..51bcc31 100644 --- a/apps/base/jellyfin/pvc-media-tv.yaml +++ b/apps/base/jellyfin/pvc-media-tv.yaml @@ -7,6 +7,11 @@ kind: PersistentVolumeClaim metadata: name: jellyfin-media-tv namespace: jellyfin + annotations: + # Exclude from the jellyfin-config k8up Schedule (skipWithoutAnnotation is + # false cluster-wide, so unannotated PVCs are swept in). Only jellyfin-config + # is backed up; the media library is not restic-backup material. + k8up.io/backup: "false" spec: accessModes: - ReadWriteMany diff --git a/apps/base/jellyfin/pvc-transcode.yaml b/apps/base/jellyfin/pvc-transcode.yaml index d23721a..01750ad 100644 --- a/apps/base/jellyfin/pvc-transcode.yaml +++ b/apps/base/jellyfin/pvc-transcode.yaml @@ -8,6 +8,11 @@ kind: PersistentVolumeClaim metadata: name: jellyfin-transcode namespace: jellyfin + annotations: + # Exclude from the jellyfin-config k8up Schedule (skipWithoutAnnotation is + # false cluster-wide, so unannotated PVCs are swept in). Transcode is RWX + # scratch — nothing to back up. + k8up.io/backup: "false" spec: accessModes: - ReadWriteMany diff --git a/apps/base/jellyfin/redis-pvc.yaml b/apps/base/jellyfin/redis-pvc.yaml index 36a4315..a6717ad 100644 --- a/apps/base/jellyfin/redis-pvc.yaml +++ b/apps/base/jellyfin/redis-pvc.yaml @@ -4,6 +4,11 @@ kind: PersistentVolumeClaim metadata: name: jellyfin-redis-data namespace: jellyfin + annotations: + # Exclude from the jellyfin-config k8up Schedule (skipWithoutAnnotation is + # false cluster-wide, so unannotated PVCs are swept in). Redis holds only + # ephemeral transcode-lease state; RWO would also fail to mount while in use. + k8up.io/backup: "false" spec: accessModes: - ReadWriteOnce diff --git a/apps/base/jellyfin/statefulset.yaml b/apps/base/jellyfin/statefulset.yaml index 8a6ad9e..11fdbd4 100644 --- a/apps/base/jellyfin/statefulset.yaml +++ b/apps/base/jellyfin/statefulset.yaml @@ -230,6 +230,11 @@ spec: # Per-pod scratch cache — RWO, disposable, one PVC per replica. - metadata: name: cache + annotations: + # Exclude the per-pod cache PVCs from the jellyfin-config k8up Schedule + # (skipWithoutAnnotation is false cluster-wide). Cache is disposable and + # RWO — it would also fail to mount into the backup pod while in use. + k8up.io/backup: "false" spec: accessModes: - ReadWriteOnce