Point media apps at the real mediafs library via a static CephFS PV
ci/woodpecker/pr/vector-test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/kubeconform Pipeline was successful

Why:
The media apps must serve and manage the actual media library, not empty
volumes. That library already exists on the puppet-managed CephFS filesystem
mediafs (mounted by the VM/incus instances at /shared/media) and is in active
use, so the k8s apps must mount it in place rather than provision fresh storage.

How:
- Replace the two fresh movies/tvseries PVCs with one static CephFS
  PersistentVolume bound to mediafs and a single RWX media-library claim the
  whole stack shares.
- Set the PV reclaim policy to Retain and mark it staticVolume so ceph-csi only
  mounts the pre-existing storage and can never provision or reclaim it;
  deleting the PVC or PV cannot destroy the underlying library.
- Reuse the live csi-cephfs cluster parameters (clusterID cephfs_csi_ssd_ec_4_1
  for mon discovery, csi-cephfs/csi-cephfs-secret node-stage secret) with
  fsName mediafs and rootPath / (the mediafs root that maps to /shared/media).
- Mount the library into each app by subPath so the tree matches the VM
  layout: sonarr /mnt/tvseries (tvseries), radarr /mnt/movies (movies),
  jellyfin and nzbget both subtrees; prowlarr keeps no library mount. The
  jellyfin transcode PVC stays a fresh scratch volume.
- Whitelist PersistentVolume in the media AppProject so the cluster-scoped PV
  can sync.
This commit is contained in:
Ben Vincent
2026-08-09 13:39:40 +10:00
parent e03aeca101
commit a52a419dfd
10 changed files with 84 additions and 54 deletions
+6 -7
View File
@@ -76,10 +76,12 @@ spec:
mountPath: /cache mountPath: /cache
- name: transcode - name: transcode
mountPath: /transcode mountPath: /transcode
- name: movies - name: media-library
mountPath: /mnt/movies mountPath: /mnt/movies
- name: tvseries subPath: movies
- name: media-library
mountPath: /mnt/tvseries mountPath: /mnt/tvseries
subPath: tvseries
volumes: volumes:
- name: config - name: config
persistentVolumeClaim: persistentVolumeClaim:
@@ -90,9 +92,6 @@ spec:
- name: transcode - name: transcode
persistentVolumeClaim: persistentVolumeClaim:
claimName: jellyfin-transcode claimName: jellyfin-transcode
- name: movies - name: media-library
persistentVolumeClaim: persistentVolumeClaim:
claimName: movies claimName: media-library
- name: tvseries
persistentVolumeClaim:
claimName: tvseries
+2 -2
View File
@@ -6,5 +6,5 @@ resources:
- namespace.yaml - namespace.yaml
- serviceaccount.yaml - serviceaccount.yaml
- vaultauth.yaml - vaultauth.yaml
- pvc_movies.yaml - pv_media-library.yaml
- pvc_tvseries.yaml - pvc_media-library.yaml
@@ -0,0 +1,41 @@
---
# Static CephFS PersistentVolume bound to the pre-existing, ACTIVELY-USED
# puppet media library (ceph filesystem "mediafs", mounted by the VM/incus
# instances at /shared/media). ceph-csi only mounts this volume; staticVolume
# tells it the storage pre-exists and it must never provision or delete it.
#
# reclaimPolicy MUST stay Retain: deleting this PV or its PVC must NEVER be able
# to reclaim or destroy the underlying CephFS data that the VM instances use.
apiVersion: v1
kind: PersistentVolume
metadata:
name: media-apps-media-library
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 10Ti
# Load-bearing safety control. Do not change to Delete.
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
volumeMode: Filesystem
# Pre-bind to the media-library claim so nothing else can grab this PV.
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: media-library
namespace: media-apps
csi:
driver: cephfs.csi.ceph.com
volumeHandle: media-apps-media-library-static
nodeStageSecretRef:
name: csi-cephfs-secret
namespace: csi-cephfs
volumeAttributes:
# clusterID maps (in the csi-cephfs ceph-csi-config) to the mon set that
# also serves mediafs; for a static volume only the mon lookup is used.
clusterID: cephfs_csi_ssd_ec_4_1
fsName: mediafs
staticVolume: "true"
# Filesystem-internal root of the library (mediafs root == /shared/media).
rootPath: /
@@ -0,0 +1,19 @@
---
# Claim bound to the static mediafs PV. RWX so every app in the stack shares the
# one library. storageClassName "" + volumeName pin it to the static PV (no
# dynamic provisioning). Deleting this claim cannot reclaim the data (PV is
# Retain).
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: media-library
namespace: media-apps
spec:
accessModes:
- ReadWriteMany
storageClassName: ""
volumeName: media-apps-media-library
resources:
requests:
storage: 10Ti
volumeMode: Filesystem
-16
View File
@@ -1,16 +0,0 @@
---
# Shared movies library, read-write-many across the *arr stack and jellyfin.
# Greenfield-empty: a fresh CephFS volume, not the puppet mediafs library.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: movies
namespace: media-apps
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 500Gi
storageClassName: cephfs-raid5-retain
volumeMode: Filesystem
-16
View File
@@ -1,16 +0,0 @@
---
# Shared tvseries library, read-write-many across the *arr stack and jellyfin.
# Greenfield-empty: a fresh CephFS volume, not the puppet mediafs library.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: tvseries
namespace: media-apps
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 500Gi
storageClassName: cephfs-raid5-retain
volumeMode: Filesystem
+6 -7
View File
@@ -65,17 +65,16 @@ spec:
volumeMounts: volumeMounts:
- name: config - name: config
mountPath: /config mountPath: /config
- name: movies - name: media-library
mountPath: /mnt/movies mountPath: /mnt/movies
- name: tvseries subPath: movies
- name: media-library
mountPath: /mnt/tvseries mountPath: /mnt/tvseries
subPath: tvseries
volumes: volumes:
- name: config - name: config
persistentVolumeClaim: persistentVolumeClaim:
claimName: nzbget-config claimName: nzbget-config
- name: movies - name: media-library
persistentVolumeClaim: persistentVolumeClaim:
claimName: movies claimName: media-library
- name: tvseries
persistentVolumeClaim:
claimName: tvseries
+4 -3
View File
@@ -78,8 +78,9 @@ spec:
volumeMounts: volumeMounts:
- name: config - name: config
mountPath: /config mountPath: /config
- name: movies - name: media-library
mountPath: /mnt/movies mountPath: /mnt/movies
subPath: movies
- name: exportarr - name: exportarr
image: ghcr.io/onedr0p/exportarr:latest image: ghcr.io/onedr0p/exportarr:latest
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
@@ -134,6 +135,6 @@ spec:
- name: config-template - name: config-template
configMap: configMap:
name: radarr-config-xml name: radarr-config-xml
- name: movies - name: media-library
persistentVolumeClaim: persistentVolumeClaim:
claimName: movies claimName: media-library
+4 -3
View File
@@ -78,8 +78,9 @@ spec:
volumeMounts: volumeMounts:
- name: config - name: config
mountPath: /config mountPath: /config
- name: tvseries - name: media-library
mountPath: /mnt/tvseries mountPath: /mnt/tvseries
subPath: tvseries
- name: exportarr - name: exportarr
image: ghcr.io/onedr0p/exportarr:latest image: ghcr.io/onedr0p/exportarr:latest
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
@@ -134,6 +135,6 @@ spec:
- name: config-template - name: config-template
configMap: configMap:
name: sonarr-config-xml name: sonarr-config-xml
- name: tvseries - name: media-library
persistentVolumeClaim: persistentVolumeClaim:
claimName: tvseries claimName: media-library
+2
View File
@@ -14,6 +14,8 @@ spec:
clusterResourceWhitelist: clusterResourceWhitelist:
- group: '' - group: ''
kind: Namespace kind: Namespace
- group: ''
kind: PersistentVolume
namespaceResourceWhitelist: namespaceResourceWhitelist:
- group: '*' - group: '*'
kind: '*' kind: '*'