Why:
Jellyfin ships and gets validated first, ahead of the rest of the media stack.
Scoping this PR to jellyfin alone keeps the initial rollout small and lets the
HA fork prove out against the real library before the download and manager apps
follow.
How:
- Drop sonarr, radarr, prowlarr, bazarr, nzbget, and jellyseerr and their shared
media-apps foundation from this PR; they land in later PRs.
- Move jellyfin into its own jellyfin namespace and fold the namespace and the
static mediafs PV plus its RWX claim into the jellyfin base.
- Keep the static CephFS PV bound to the in-use mediafs library with
reclaimPolicy Retain and staticVolume true so nothing can reclaim it, mounted
into jellyfin by the movies and tvseries subPaths; keep redis, the fresh RWX
transcode scratch, the intel iGPU nodeSelector and i915 request, gateway, and
httproute.
- Scope the media AppProject and ApplicationSet to the single jellyfin
namespace and app, extensible as the remaining apps are added.
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.
Why:
The media stack (jellyfin plus the sonarr/radarr/prowlarr/bazarr/nzbget/
jellyseerr apps) runs in the media-apps namespace but is deployed out-of-band
by terraform-k8s rather than GitOps. Bringing it under ArgoCD makes the stack
declarative, self-healing, and consistent with every other cluster workload,
and prepares terraform-k8s to drop the media-apps config.
How:
- Add a media AppProject scoped to the media-apps namespace and a media-apps
ApplicationSet that renders one Application per app plus a shared foundation.
- Add a shared media-apps foundation (namespace, media-apps-vault-reader
ServiceAccount, default VaultAuth on k8s/au/syd1, and the RWX movies/tvseries
library PVCs) that the whole stack mounts.
- Add per-app kustomize base and au-syd1 overlay for jellyfin and the six *arr
apps, using plain resource names (jellyfin, sonarr, ...) with fresh PVCs.
- Deploy jellyfin from the jellyfin-ha fork (Redis transcode store, RWX
transcode scratch) wired to the shared movies/tvseries library PVCs, keeping
the intel iGPU nodeSelector and gpu.intel.com/i915 request.
- Source API keys and nzbget credentials through VSO VaultStaticSecrets from
kv/service/media-apps/<app>; expose each app via a traefik-internal Gateway
and HTTPRoute at <app>.k8s.syd1.au.unkin.net.
- Register the media project and applicationset in the argocd bootstrap
kustomizations.