Replace legacy jellyfin app with fafflix (adult, cheeztv pattern)
Rebuild the adult media instance as 'fafflix' following the same HA pattern
as the kids instance (cheeztv): Postgres-backed jellyfin-ha fork, Valkey
transcode-lease store, shared-RWX transcode, CNPG + k8up/restic backups,
static CephFS media PVs.
- Add apps/base/fafflix (namespace/labels/secrets/buckets/Vault path fafflix-*)
- fafflix mounts the shared movies/tv subvolumes' adult subtree at
/media/{movies,tv} plus the kids subtree at /media/{movies,tv}-kids so it can
resume kids content started on cheeztv; new unique static PV volumeHandles
- Keep serving the legacy hostname jellyfin.k8s.syd1.au.unkin.net (fafflix-tls);
dedicated fafflix domain deferred, no new public host
- config PVC on cephfs-raid5-delete
- Remove apps/base/jellyfin + overlay; swap jellyfin->fafflix in the media
ApplicationSet glob and AppProject namespace destination
- No data currently on the adult instance, so the wipe/replace is sanctioned
This commit is contained in:
+12
-12
@@ -1,32 +1,32 @@
|
||||
---
|
||||
# Second Ceph RGW (S3) bucket owned by the existing jellyfin backup user
|
||||
# (cnpg-jellyfin-backup, defined in cnpg_backup.yaml) — one user, two buckets:
|
||||
# Second Ceph RGW (S3) bucket owned by the existing fafflix backup user
|
||||
# (cnpg-fafflix-backup, defined in cnpg_backup.yaml) — one user, two buckets:
|
||||
# the CNPG barman bucket plus this one, which k8up uses to hold restic backups
|
||||
# of the jellyfin-config PVC. The BucketAccess emits read-write S3 creds into a
|
||||
# of the fafflix-config PVC. The BucketAccess emits read-write S3 creds into a
|
||||
# Secret the k8up Schedule consumes.
|
||||
apiVersion: ceph.unkin.net/v1alpha1
|
||||
kind: Bucket
|
||||
metadata:
|
||||
name: jellyfin-config-backup
|
||||
namespace: jellyfin
|
||||
name: fafflix-config-backup
|
||||
namespace: fafflix
|
||||
spec:
|
||||
placementTarget: ec
|
||||
bucketName: jellyfin-config-backup
|
||||
ownerRef: cnpg-jellyfin-backup
|
||||
bucketName: fafflix-config-backup
|
||||
ownerRef: cnpg-fafflix-backup
|
||||
versioning: false
|
||||
tags:
|
||||
app: jellyfin
|
||||
app: fafflix
|
||||
purpose: config-backup
|
||||
retainOnDelete: true
|
||||
---
|
||||
apiVersion: ceph.unkin.net/v1alpha1
|
||||
kind: BucketAccess
|
||||
metadata:
|
||||
name: jellyfin-config-backup
|
||||
namespace: jellyfin
|
||||
name: fafflix-config-backup
|
||||
namespace: fafflix
|
||||
spec:
|
||||
bucketRef: jellyfin-config-backup
|
||||
bucketRef: fafflix-config-backup
|
||||
level: read-write
|
||||
# Operator writes AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY (+ S3_ENDPOINT,
|
||||
# BUCKET_NAME) into this Secret; the k8up Schedule reads the access keys.
|
||||
secretName: jellyfin-config-backup-s3
|
||||
secretName: fafflix-config-backup-s3
|
||||
@@ -1,31 +1,31 @@
|
||||
---
|
||||
# Ceph RGW (S3) backup target for the jellyfin CNPG cluster, provisioned by the
|
||||
# Ceph RGW (S3) backup target for the fafflix CNPG cluster, provisioned by the
|
||||
# in-estate cephrgw-operator: one dedicated bucket + owner user. CNPG reads the
|
||||
# S3 credential Secret from its own namespace.
|
||||
apiVersion: ceph.unkin.net/v1alpha1
|
||||
kind: ObjectStoreUser
|
||||
metadata:
|
||||
name: cnpg-jellyfin-backup
|
||||
namespace: jellyfin
|
||||
name: cnpg-fafflix-backup
|
||||
namespace: fafflix
|
||||
spec:
|
||||
displayName: "CNPG backup owner (jellyfin)"
|
||||
uid: cnpg-jellyfin-backup
|
||||
displayName: "CNPG backup owner (fafflix)"
|
||||
uid: cnpg-fafflix-backup
|
||||
maxBuckets: 5
|
||||
secretName: cnpg-jellyfin-backup-s3
|
||||
secretName: cnpg-fafflix-backup-s3
|
||||
retainOnDelete: true
|
||||
---
|
||||
apiVersion: ceph.unkin.net/v1alpha1
|
||||
kind: Bucket
|
||||
metadata:
|
||||
name: cnpg-jellyfin
|
||||
namespace: jellyfin
|
||||
name: cnpg-fafflix
|
||||
namespace: fafflix
|
||||
spec:
|
||||
placementTarget: ec
|
||||
bucketName: cnpg-jellyfin
|
||||
ownerRef: cnpg-jellyfin-backup
|
||||
bucketName: cnpg-fafflix
|
||||
ownerRef: cnpg-fafflix-backup
|
||||
versioning: false
|
||||
tags:
|
||||
app: jellyfin
|
||||
app: fafflix
|
||||
purpose: cnpg-backup
|
||||
retainOnDelete: true
|
||||
---
|
||||
@@ -34,12 +34,12 @@ spec:
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: ScheduledBackup
|
||||
metadata:
|
||||
name: cnpg-jellyfin-nightly
|
||||
namespace: jellyfin
|
||||
name: cnpg-fafflix-nightly
|
||||
namespace: fafflix
|
||||
spec:
|
||||
schedule: "0 35 3 * * *"
|
||||
immediate: false
|
||||
backupOwnerReference: self
|
||||
method: barmanObjectStore
|
||||
cluster:
|
||||
name: jellyfin-postgres
|
||||
name: fafflix-postgres
|
||||
@@ -1,17 +1,17 @@
|
||||
---
|
||||
# Main Jellyfin database. The jellyfin-ha fork's experimental EF Core provider
|
||||
# Main Jellyfin database. The fafflix-ha fork's experimental EF Core provider
|
||||
# moves the entire Jellyfin DB (incl. library items) off SQLite into PostgreSQL,
|
||||
# which is what makes a shared-nothing multi-replica deployment possible. No
|
||||
# bootstrap secret is given, so CNPG generates the jellyfin-postgres-app secret
|
||||
# bootstrap secret is given, so CNPG generates the fafflix-postgres-app secret
|
||||
# (username/password/dbname) that the StatefulSet composes its DSN from.
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: jellyfin-postgres
|
||||
namespace: jellyfin
|
||||
name: fafflix-postgres
|
||||
namespace: fafflix
|
||||
spec:
|
||||
# Exclude the operator-managed data PVCs (jellyfin-postgres-N) from the
|
||||
# jellyfin-config k8up Schedule (skipWithoutAnnotation is false cluster-wide,
|
||||
# Exclude the operator-managed data PVCs (fafflix-postgres-N) from the
|
||||
# fafflix-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:
|
||||
@@ -23,19 +23,19 @@ spec:
|
||||
retentionPolicy: 30d
|
||||
barmanObjectStore:
|
||||
# Dedicated per-cluster Ceph RGW bucket (cephrgw-operator provisions it).
|
||||
destinationPath: s3://cnpg-jellyfin
|
||||
destinationPath: s3://cnpg-fafflix
|
||||
endpointURL: https://s3.ceph.unkin.net
|
||||
endpointCA:
|
||||
name: vault-ca-cert
|
||||
key: ca.crt
|
||||
s3Credentials:
|
||||
accessKeyId:
|
||||
name: cnpg-jellyfin-backup-s3
|
||||
name: cnpg-fafflix-backup-s3
|
||||
key: AWS_ACCESS_KEY_ID
|
||||
secretAccessKey:
|
||||
name: cnpg-jellyfin-backup-s3
|
||||
name: cnpg-fafflix-backup-s3
|
||||
key: AWS_SECRET_ACCESS_KEY
|
||||
serverName: jellyfin
|
||||
serverName: fafflix
|
||||
data:
|
||||
compression: bzip2
|
||||
jobs: 2
|
||||
@@ -44,11 +44,11 @@ spec:
|
||||
maxParallel: 2
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: jellyfin
|
||||
database: fafflix
|
||||
encoding: UTF8
|
||||
localeCType: C
|
||||
localeCollate: C
|
||||
owner: jellyfin
|
||||
owner: fafflix
|
||||
enablePDB: true
|
||||
enableSuperuserAccess: false
|
||||
failoverDelay: 0
|
||||
@@ -1,15 +1,15 @@
|
||||
---
|
||||
# PgBouncer pooler in front of the jellyfin-postgres cluster. Jellyfin connects
|
||||
# here (jellyfin-postgres-pooler:5432) rather than the -rw service so EF Core's
|
||||
# PgBouncer pooler in front of the fafflix-postgres cluster. Jellyfin connects
|
||||
# here (fafflix-postgres-pooler:5432) rather than the -rw service so EF Core's
|
||||
# connection churn is absorbed by the pool.
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Pooler
|
||||
metadata:
|
||||
name: jellyfin-postgres-pooler
|
||||
namespace: jellyfin
|
||||
name: fafflix-postgres-pooler
|
||||
namespace: fafflix
|
||||
spec:
|
||||
cluster:
|
||||
name: jellyfin-postgres
|
||||
name: fafflix-postgres
|
||||
instances: 2
|
||||
pgbouncer:
|
||||
parameters:
|
||||
@@ -20,7 +20,7 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: jellyfin-pooler
|
||||
app: fafflix-pooler
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
@@ -30,7 +30,7 @@ spec:
|
||||
- key: app
|
||||
operator: In
|
||||
values:
|
||||
- jellyfin-pooler
|
||||
- fafflix-pooler
|
||||
topologyKey: kubernetes.io/hostname
|
||||
containers: []
|
||||
type: rw
|
||||
@@ -1,4 +1,9 @@
|
||||
---
|
||||
# Fafflix (adult instance) keeps serving the legacy jellyfin hostname
|
||||
# (jellyfin.k8s.syd1.au.unkin.net) so the switch to a dedicated fafflix domain
|
||||
# can be deferred. Same internal-Traefik + external-dns pattern the old jellyfin app
|
||||
# used: external-dns publishes the A record at the internal LB VIP
|
||||
# (198.18.200.4) and cert-manager mints fafflix-tls off the Vault-PKI issuer.
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
@@ -10,8 +15,8 @@ metadata:
|
||||
cert-manager.io/private-key-size: "4096"
|
||||
external-dns.alpha.kubernetes.io/hostname: jellyfin.k8s.syd1.au.unkin.net
|
||||
external-dns.alpha.kubernetes.io/target: 198.18.200.4
|
||||
name: jellyfin
|
||||
namespace: jellyfin
|
||||
name: fafflix
|
||||
namespace: fafflix
|
||||
spec:
|
||||
gatewayClassName: traefik-internal
|
||||
listeners:
|
||||
@@ -33,5 +38,5 @@ spec:
|
||||
certificateRefs:
|
||||
- group: ""
|
||||
kind: Secret
|
||||
name: jellyfin-tls
|
||||
name: fafflix-tls
|
||||
mode: Terminate
|
||||
@@ -3,14 +3,14 @@ apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: http-redirect
|
||||
namespace: jellyfin
|
||||
namespace: fafflix
|
||||
spec:
|
||||
hostnames:
|
||||
- jellyfin.k8s.syd1.au.unkin.net
|
||||
parentRefs:
|
||||
- group: gateway.networking.k8s.io
|
||||
kind: Gateway
|
||||
name: jellyfin
|
||||
name: fafflix
|
||||
sectionName: http
|
||||
rules:
|
||||
- filters:
|
||||
@@ -26,21 +26,21 @@ spec:
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: jellyfin-route
|
||||
namespace: jellyfin
|
||||
name: fafflix-route
|
||||
namespace: fafflix
|
||||
spec:
|
||||
hostnames:
|
||||
- jellyfin.k8s.syd1.au.unkin.net
|
||||
parentRefs:
|
||||
- group: gateway.networking.k8s.io
|
||||
kind: Gateway
|
||||
name: jellyfin
|
||||
name: fafflix
|
||||
sectionName: https
|
||||
rules:
|
||||
- backendRefs:
|
||||
- group: ""
|
||||
kind: Service
|
||||
name: jellyfin
|
||||
name: fafflix
|
||||
port: 8096
|
||||
weight: 1
|
||||
matches:
|
||||
@@ -2,4 +2,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: jellyfin
|
||||
name: fafflix
|
||||
@@ -4,10 +4,10 @@
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: jellyfin
|
||||
namespace: jellyfin
|
||||
name: fafflix
|
||||
namespace: fafflix
|
||||
spec:
|
||||
minAvailable: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: jellyfin
|
||||
app: fafflix
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
# Static PV for the shared MOVIES CephFS subvolume. Same rootPath as arrstack's
|
||||
# movies PV so radarr writes and jellyfin reads the identical library tree; each
|
||||
# movies PV so radarr writes and fafflix reads the identical library tree; each
|
||||
# namespace gets its own PV (unique name + volumeHandle) pinned by claimRef.
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: jellyfin-media-movies
|
||||
name: fafflix-media-movies
|
||||
spec:
|
||||
capacity:
|
||||
storage: 1Ti
|
||||
@@ -15,11 +15,11 @@ spec:
|
||||
storageClassName: ""
|
||||
volumeMode: Filesystem
|
||||
claimRef:
|
||||
namespace: jellyfin
|
||||
name: jellyfin-media-movies
|
||||
namespace: fafflix
|
||||
name: fafflix-media-movies
|
||||
csi:
|
||||
driver: cephfs.csi.ceph.com
|
||||
volumeHandle: jellyfin-media-movies-static
|
||||
volumeHandle: fafflix-media-movies-static
|
||||
nodeStageSecretRef:
|
||||
name: csi-cephfs-secret
|
||||
namespace: csi-cephfs
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
# Static PV for the shared TV CephFS subvolume. Same rootPath as arrstack's
|
||||
# TV PV so sonarr writes and jellyfin reads the identical library tree; each
|
||||
# TV PV so sonarr writes and fafflix reads the identical library tree; each
|
||||
# namespace gets its own PV (unique name + volumeHandle) pinned by claimRef.
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: jellyfin-media-tv
|
||||
name: fafflix-media-tv
|
||||
spec:
|
||||
capacity:
|
||||
storage: 1Ti
|
||||
@@ -15,11 +15,11 @@ spec:
|
||||
storageClassName: ""
|
||||
volumeMode: Filesystem
|
||||
claimRef:
|
||||
namespace: jellyfin
|
||||
name: jellyfin-media-tv
|
||||
namespace: fafflix
|
||||
name: fafflix-media-tv
|
||||
csi:
|
||||
driver: cephfs.csi.ceph.com
|
||||
volumeHandle: jellyfin-media-tv-static
|
||||
volumeHandle: fafflix-media-tv-static
|
||||
nodeStageSecretRef:
|
||||
name: csi-cephfs-secret
|
||||
namespace: csi-cephfs
|
||||
@@ -1,17 +1,17 @@
|
||||
---
|
||||
# Jellyfin config: metadata images, plugins, subtitles and config XML. Shared
|
||||
# ReadWriteMany across replicas (all pods read/write the same library metadata);
|
||||
# the main library DB now lives in PostgreSQL, not here. Retain — this is state.
|
||||
# the main library DB now lives in PostgreSQL, not here (on CephFS, raid5-delete).
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: jellyfin-config
|
||||
namespace: jellyfin
|
||||
name: fafflix-config
|
||||
namespace: fafflix
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 20Gi
|
||||
storageClassName: cephfs-raid5-retain
|
||||
storageClassName: cephfs-raid5-delete
|
||||
volumeMode: Filesystem
|
||||
@@ -1,16 +1,16 @@
|
||||
---
|
||||
# Movie library, shared read-many across replicas. Statically bound to the
|
||||
# jellyfin-media-movies PV (shared CephFS subvolume also used by arrstack/radarr).
|
||||
# fafflix-media-movies PV (shared CephFS subvolume also used by arrstack/radarr).
|
||||
# storageClassName "" + volumeName disables dynamic provisioning and binds the
|
||||
# pre-created static PV.
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: jellyfin-media-movies
|
||||
namespace: jellyfin
|
||||
name: fafflix-media-movies
|
||||
namespace: fafflix
|
||||
annotations:
|
||||
# Exclude from the jellyfin-config k8up Schedule (skipWithoutAnnotation is
|
||||
# false cluster-wide, so unannotated PVCs are swept in). Only jellyfin-config
|
||||
# Exclude from the fafflix-config k8up Schedule (skipWithoutAnnotation is
|
||||
# false cluster-wide, so unannotated PVCs are swept in). Only fafflix-config
|
||||
# is backed up; the media library is not restic-backup material.
|
||||
k8up.io/backup: "false"
|
||||
spec:
|
||||
@@ -20,5 +20,5 @@ spec:
|
||||
requests:
|
||||
storage: 1Ti
|
||||
storageClassName: ""
|
||||
volumeName: jellyfin-media-movies
|
||||
volumeName: fafflix-media-movies
|
||||
volumeMode: Filesystem
|
||||
@@ -1,16 +1,16 @@
|
||||
---
|
||||
# TV library, shared read-many across replicas. Statically bound to the
|
||||
# jellyfin-media-tv PV (shared CephFS subvolume also used by arrstack/sonarr).
|
||||
# fafflix-media-tv PV (shared CephFS subvolume also used by arrstack/sonarr).
|
||||
# storageClassName "" + volumeName disables dynamic provisioning and binds the
|
||||
# pre-created static PV.
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: jellyfin-media-tv
|
||||
namespace: jellyfin
|
||||
name: fafflix-media-tv
|
||||
namespace: fafflix
|
||||
annotations:
|
||||
# Exclude from the jellyfin-config k8up Schedule (skipWithoutAnnotation is
|
||||
# false cluster-wide, so unannotated PVCs are swept in). Only jellyfin-config
|
||||
# Exclude from the fafflix-config k8up Schedule (skipWithoutAnnotation is
|
||||
# false cluster-wide, so unannotated PVCs are swept in). Only fafflix-config
|
||||
# is backed up; the media library is not restic-backup material.
|
||||
k8up.io/backup: "false"
|
||||
spec:
|
||||
@@ -20,5 +20,5 @@ spec:
|
||||
requests:
|
||||
storage: 1Ti
|
||||
storageClassName: ""
|
||||
volumeName: jellyfin-media-tv
|
||||
volumeName: fafflix-media-tv
|
||||
volumeMode: Filesystem
|
||||
@@ -6,10 +6,10 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: jellyfin-transcode
|
||||
namespace: jellyfin
|
||||
name: fafflix-transcode
|
||||
namespace: fafflix
|
||||
annotations:
|
||||
# Exclude from the jellyfin-config k8up Schedule (skipWithoutAnnotation is
|
||||
# Exclude from the fafflix-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"
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
# k8up Schedule: restic backups of the jellyfin-config PVC (library metadata,
|
||||
# k8up Schedule: restic backups of the fafflix-config PVC (library metadata,
|
||||
# plugins, config XML) to the dedicated Ceph RGW config-backup bucket. S3 creds
|
||||
# come from the cephrgw BucketAccess Secret (jellyfin-config-backup-s3); the
|
||||
# restic repo password comes from Vault via the jellyfin-k8up-restic Secret.
|
||||
# come from the cephrgw BucketAccess Secret (fafflix-config-backup-s3); the
|
||||
# restic repo password comes from Vault via the fafflix-k8up-restic Secret.
|
||||
#
|
||||
# s3.ceph.unkin.net presents the internal unkin.net CA, which the k8up/restic
|
||||
# image does not trust by default, so the reflected vault-ca-cert Secret is
|
||||
@@ -10,21 +10,21 @@
|
||||
apiVersion: k8up.io/v1
|
||||
kind: Schedule
|
||||
metadata:
|
||||
name: jellyfin-config
|
||||
namespace: jellyfin
|
||||
name: fafflix-config
|
||||
namespace: fafflix
|
||||
spec:
|
||||
backend:
|
||||
repoPasswordSecretRef:
|
||||
name: jellyfin-k8up-restic
|
||||
name: fafflix-k8up-restic
|
||||
key: password
|
||||
s3:
|
||||
endpoint: https://s3.ceph.unkin.net
|
||||
bucket: jellyfin-config-backup
|
||||
bucket: fafflix-config-backup
|
||||
accessKeyIDSecretRef:
|
||||
name: jellyfin-config-backup-s3
|
||||
name: fafflix-config-backup-s3
|
||||
key: AWS_ACCESS_KEY_ID
|
||||
secretAccessKeySecretRef:
|
||||
name: jellyfin-config-backup-s3
|
||||
name: fafflix-config-backup-s3
|
||||
key: AWS_SECRET_ACCESS_KEY
|
||||
tlsOptions:
|
||||
caCert: /etc/k8up/ca/ca.crt
|
||||
@@ -2,8 +2,8 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jellyfin
|
||||
namespace: jellyfin
|
||||
name: fafflix
|
||||
namespace: fafflix
|
||||
spec:
|
||||
internalTrafficPolicy: Cluster
|
||||
ports:
|
||||
@@ -12,7 +12,7 @@ spec:
|
||||
protocol: TCP
|
||||
targetPort: http
|
||||
selector:
|
||||
app: jellyfin
|
||||
app: fafflix
|
||||
# Pin each client to one replica to reduce transcode-session churn/takeover.
|
||||
sessionAffinity: ClientIP
|
||||
type: ClusterIP
|
||||
@@ -2,24 +2,24 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: jellyfin
|
||||
namespace: jellyfin
|
||||
name: fafflix
|
||||
namespace: fafflix
|
||||
spec:
|
||||
# HA: two replicas coordinate transcode session ownership through Valkey and
|
||||
# resume each other's HLS segments off the shared RWX transcode PVC. Stable
|
||||
# pod names (jellyfin-0/1) are the lease owner identity, hence StatefulSet.
|
||||
# pod names (fafflix-0/1) are the lease owner identity, hence StatefulSet.
|
||||
replicas: 2
|
||||
serviceName: jellyfin
|
||||
serviceName: fafflix
|
||||
podManagementPolicy: Parallel
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: jellyfin
|
||||
app: fafflix
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: jellyfin
|
||||
app: fafflix
|
||||
spec:
|
||||
securityContext:
|
||||
# Group-write the shared RWX volumes and grant the render/video groups so
|
||||
@@ -41,7 +41,7 @@ spec:
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app: jellyfin
|
||||
app: fafflix
|
||||
topologyKey: kubernetes.io/hostname
|
||||
initContainers:
|
||||
# Seed the fork's PostgreSQL provider (database.xml) and Intel iGPU
|
||||
@@ -110,7 +110,7 @@ spec:
|
||||
- name: config
|
||||
mountPath: /config
|
||||
containers:
|
||||
- name: jellyfin
|
||||
- name: fafflix
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/jellyfin-ha:v0.1.3
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
@@ -137,9 +137,9 @@ spec:
|
||||
# Config dir must differ from the data root (Jellyfin sanity check).
|
||||
- name: JELLYFIN_CONFIG_DIR
|
||||
value: /config/config
|
||||
# Distributed transcode session store (jellyfin-ha additions).
|
||||
# Distributed transcode session store (fafflix-ha additions).
|
||||
- name: Jellyfin__TranscodeStore__RedisConnectionString
|
||||
value: "valkey-jellyfin-valkey:6379,abortConnect=false"
|
||||
value: "valkey-fafflix-valkey:6379,abortConnect=false"
|
||||
- name: Jellyfin__TranscodeStore__LeaseDurationSeconds
|
||||
value: "30"
|
||||
# PostgreSQL main DB via the CNPG-generated app secret, routed through
|
||||
@@ -148,22 +148,22 @@ spec:
|
||||
- name: PGUSER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: jellyfin-postgres-app
|
||||
name: fafflix-postgres-app
|
||||
key: username
|
||||
- name: PGPASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: jellyfin-postgres-app
|
||||
name: fafflix-postgres-app
|
||||
key: password
|
||||
- name: PGDB
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: jellyfin-postgres-app
|
||||
name: fafflix-postgres-app
|
||||
key: dbname
|
||||
- name: POSTGRES_CONNECTION_STRING
|
||||
value: "postgresql://$(PGUSER):$(PGPASSWORD)@jellyfin-postgres-pooler:5432/$(PGDB)"
|
||||
value: "postgresql://$(PGUSER):$(PGPASSWORD)@fafflix-postgres-pooler:5432/$(PGDB)"
|
||||
- name: DATABASE_URL
|
||||
value: "postgresql://$(PGUSER):$(PGPASSWORD)@jellyfin-postgres-pooler:5432/$(PGDB)"
|
||||
value: "postgresql://$(PGUSER):$(PGPASSWORD)@fafflix-postgres-pooler:5432/$(PGDB)"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
@@ -208,42 +208,52 @@ spec:
|
||||
- name: cache
|
||||
mountPath: /cache
|
||||
- name: media-tv
|
||||
# Adult instance: mount the tvshows/adult subtree of the shared TV
|
||||
# subvolume (subPath adult) as fafflix's primary TV library. Same
|
||||
# rootPath the cheeztv instance reads under subPath kids, so an
|
||||
# episode resolves identically across instances.
|
||||
mountPath: /media/tv
|
||||
subPath: adult
|
||||
readOnly: true
|
||||
- name: media-movies
|
||||
mountPath: /media/movies
|
||||
readOnly: true
|
||||
# Kids subtrees surfaced as their own paths (same media PVCs, subPath
|
||||
# kids) so a "Kids TV"/"Kids Movies" library can be added here and its
|
||||
# titles are browsable and resume in this instance's own DB. The full
|
||||
# /media/{tv,movies} mounts above are unchanged.
|
||||
- name: media-tv
|
||||
# Also mount the kids TV subtree (subPath kids) so fafflix can
|
||||
# resume playback of kids content started on cheeztv — same
|
||||
# underlying subvolume, different subtree, distinct mount path.
|
||||
mountPath: /media/tv-kids
|
||||
subPath: kids
|
||||
readOnly: true
|
||||
- name: media-movies
|
||||
# Adult instance: mount the movies/adult subtree of the shared
|
||||
# movies subvolume (subPath adult) as fafflix's primary movie
|
||||
# library.
|
||||
mountPath: /media/movies
|
||||
subPath: adult
|
||||
readOnly: true
|
||||
- name: media-movies
|
||||
# Also mount the kids movies subtree (subPath kids) for cross-resume
|
||||
# of kids content started on cheeztv.
|
||||
mountPath: /media/movies-kids
|
||||
subPath: kids
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: jellyfin-config
|
||||
claimName: fafflix-config
|
||||
- name: transcode
|
||||
persistentVolumeClaim:
|
||||
claimName: jellyfin-transcode
|
||||
claimName: fafflix-transcode
|
||||
- name: media-tv
|
||||
persistentVolumeClaim:
|
||||
claimName: jellyfin-media-tv
|
||||
claimName: fafflix-media-tv
|
||||
- name: media-movies
|
||||
persistentVolumeClaim:
|
||||
claimName: jellyfin-media-movies
|
||||
claimName: fafflix-media-movies
|
||||
volumeClaimTemplates:
|
||||
# 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
|
||||
# Exclude the per-pod cache PVCs from the fafflix-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"
|
||||
@@ -19,20 +19,18 @@
|
||||
apiVersion: valkey.io/v1alpha1
|
||||
kind: ValkeyCluster
|
||||
metadata:
|
||||
name: jellyfin-valkey
|
||||
namespace: jellyfin
|
||||
name: fafflix-valkey
|
||||
namespace: fafflix
|
||||
spec:
|
||||
shards: 1
|
||||
replicas: 2
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/valkey/valkey:9.0.0
|
||||
image: docker.io/valkey/valkey:9.0.0
|
||||
# redis_exporter sidecar (:9121, port name `metrics`) on every ValkeyNode pod;
|
||||
# the operator manages a dedicated _exporter ACL user for it. Image overridden
|
||||
# from the operator default (bare dockerhub oliver006/redis_exporter:v1.80.0)
|
||||
# to the artifactapi-proxied pin. Scraped by the valkey-exporter VMPodScrape
|
||||
# in vmpodscrape.yaml alongside this file.
|
||||
# the operator manages a dedicated _exporter ACL user for it. Image version
|
||||
# pinned; scraped by valkey-exporter VMPodScrape in vmpodscrape.yaml.
|
||||
exporter:
|
||||
enabled: true
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/oliver006/redis_exporter:v1.89.0
|
||||
image: docker.io/oliver006/redis_exporter:v1.89.0
|
||||
scheduling:
|
||||
node:
|
||||
spread:
|
||||
@@ -3,12 +3,12 @@ apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultAuth
|
||||
metadata:
|
||||
name: default
|
||||
namespace: jellyfin
|
||||
namespace: fafflix
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "0"
|
||||
spec:
|
||||
allowedNamespaces:
|
||||
- jellyfin
|
||||
- fafflix
|
||||
kubernetes:
|
||||
audiences:
|
||||
- vault
|
||||
+7
-7
@@ -1,24 +1,24 @@
|
||||
---
|
||||
# restic repository password for the k8up jellyfin-config backups. Seeded at
|
||||
# kv/kubernetes/namespace/jellyfin/default/k8up-restic (key: password); the
|
||||
# restic repository password for the k8up fafflix-config backups. Seeded at
|
||||
# kv/kubernetes/namespace/fafflix/default/k8up-restic (key: password); the
|
||||
# default k8s role's templated policy already grants read here, so no
|
||||
# terraform-vault change is needed. VSO syncs it into the jellyfin-k8up-restic
|
||||
# terraform-vault change is needed. VSO syncs it into the fafflix-k8up-restic
|
||||
# Secret that the Schedule references via backend.repoPasswordSecretRef.
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultStaticSecret
|
||||
metadata:
|
||||
name: jellyfin-k8up-restic
|
||||
namespace: jellyfin
|
||||
name: fafflix-k8up-restic
|
||||
namespace: fafflix
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "0"
|
||||
spec:
|
||||
destination:
|
||||
create: true
|
||||
name: jellyfin-k8up-restic
|
||||
name: fafflix-k8up-restic
|
||||
overwrite: true
|
||||
hmacSecretData: true
|
||||
mount: kv
|
||||
path: kubernetes/namespace/jellyfin/default/k8up-restic
|
||||
path: kubernetes/namespace/fafflix/default/k8up-restic
|
||||
refreshAfter: 5m
|
||||
type: kv-v2
|
||||
vaultAuthRef: default
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
# Scrape the operator-injected redis_exporter sidecar (:9121, port name
|
||||
# `metrics`) on the jellyfin-valkey ValkeyNode pods. The valkey-operator gives
|
||||
# `metrics`) on the fafflix-valkey ValkeyNode pods. The valkey-operator gives
|
||||
# its pods fixed labels only (no pod-label passthrough on the ValkeyCluster CR),
|
||||
# so select on the operator-managed labels. Picked up by the observability
|
||||
# VMAgent (selectAllByDefault).
|
||||
@@ -8,7 +8,7 @@ apiVersion: operator.victoriametrics.com/v1beta1
|
||||
kind: VMPodScrape
|
||||
metadata:
|
||||
name: valkey-exporter
|
||||
namespace: jellyfin
|
||||
namespace: fafflix
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
+1
-1
@@ -3,4 +3,4 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- ../../../base/jellyfin
|
||||
- ../../../base/fafflix
|
||||
@@ -10,7 +10,7 @@ spec:
|
||||
repoURL: https://git.unkin.net/unkin/argocd-apps
|
||||
revision: HEAD
|
||||
directories:
|
||||
- path: apps/overlays/*/jellyfin
|
||||
- path: apps/overlays/*/fafflix
|
||||
- path: apps/overlays/*/cheeztv
|
||||
- path: apps/overlays/*/arrstack
|
||||
template:
|
||||
|
||||
@@ -9,7 +9,7 @@ spec:
|
||||
sourceRepos:
|
||||
- https://git.unkin.net/unkin/argocd-apps
|
||||
destinations:
|
||||
- namespace: 'jellyfin'
|
||||
- namespace: 'fafflix'
|
||||
server: https://kubernetes.default.svc
|
||||
- namespace: 'cheeztv'
|
||||
server: https://kubernetes.default.svc
|
||||
|
||||
Reference in New Issue
Block a user