diff --git a/README.md b/README.md
index 13a5e73..b4b3a75 100644
--- a/README.md
+++ b/README.md
@@ -98,3 +98,22 @@ De torrent indexer proxy (`jackett`) draait als pod in de `htpc` namespace:
# Oude host bestanden en service opruimen:
./cleanup_old_jackett.sh
```
+
+### Jellyfin
+De Jellyfin media server (`jellyfin`) draait als pod in de `htpc` namespace:
+
+- **Dataopslag**: PersistentVolumeClaim `jellyfin-config` (30Gi, `local-path` op `/datadisk/infra/pv`), gemigreerd vanuit `/etc/jellyfin` en `/var/lib/jellyfin` via init-container (inclusief alle databases, metadata en plugins).
+- **Mediaopslag**: HostPath `/datadisk` gemount op `/datadisk` en `/mnt/mp3` gemount op `/mnt/mp3` zodat alle bibliotheekpaden en symlinks exact behouden blijven.
+- **Hardware Transcoding**: Directe hardware-acceleratie via VA-API met AMD Phoenix2 iGPU (`/dev/dri` hostPath gemount in container met `privileged: true`).
+- **Netwerk & Ingress**: Luistert op poort `8096` (ClusterIP + HostPort `8096`). De Apache reverse proxy routeert `https://thuus.bnhmeijer.nl/jellyfin` en `wss://.../jellyfin/socket` naadloos door naar `127.0.0.1:8096`.
+- **Migratie, Terugrol & Opruimen**:
+ ```bash
+ # Migratie uitvoeren (maakt backup in /storage/backups/, stopt host service, synct data, start pod):
+ ./migrate_jellyfin_to_cluster.sh
+
+ # Terugrollen naar standalone host service indien gewenst:
+ ./rollback_jellyfin_migration.sh
+
+ # Oude host bestanden en service opruimen:
+ ./cleanup_old_jellyfin.sh
+ ```
diff --git a/cleanup_old_jellyfin.sh b/cleanup_old_jellyfin.sh
new file mode 100755
index 0000000..af063e2
--- /dev/null
+++ b/cleanup_old_jellyfin.sh
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+# ==============================================================================
+# Opruimen van oude standalone jellyfin installatie op vmserver (10.2.0.4)
+# ==============================================================================
+set -euo pipefail
+
+CLUSTER_HOST="10.2.0.4"
+SSH_USER="bart"
+
+COLOR_GREEN="\033[0;32m"
+COLOR_YELLOW="\033[1;33m"
+COLOR_RED="\033[0;31m"
+COLOR_BLUE="\033[0;34m"
+COLOR_RESET="\033[0m"
+
+log_info() { echo -e "${COLOR_GREEN}[INFO]${COLOR_RESET} $1"; }
+log_step() { echo -e "\n${COLOR_BLUE}==>${COLOR_RESET} ${COLOR_GREEN}$1${COLOR_RESET}"; }
+log_warn() { echo -e "${COLOR_YELLOW}[WARN]${COLOR_RESET} $1"; }
+
+echo "========================================================================"
+echo " OPRUIMEN OUDE STANDALONE JELLYFIN OP ${CLUSTER_HOST}"
+echo "========================================================================"
+echo "Dit stopt en verwijdert de host-service configuratie en optioneel de"
+echo "oude data-directories op ${CLUSTER_HOST}:"
+echo " - jellyfin.service (stoppen en uitschakelen)"
+echo " - /etc/systemd/system/jellyfin.service.d"
+echo " - /var/cache/jellyfin"
+echo "LET OP: Voer dit script pas uit nadat de Kubernetes pod succesvol draait"
+echo "en geverifieerd is!"
+echo "========================================================================"
+echo ""
+
+read -p "Weet je zeker dat je de oude standalone jellyfin host service wilt opruimen? (j/N): " -r CONFIRM
+if [[ ! "$CONFIRM" =~ ^[jJyY]$ ]]; then
+ log_info "Opruimen geannuleerd."
+ exit 0
+fi
+
+log_step "Uitschakelen van jellyfin.service en opruimen van host service bestanden..."
+ssh -t "${SSH_USER}@${CLUSTER_HOST}" "
+ echo '--> Stoppen en masken van jellyfin.service...'
+ sudo systemctl stop jellyfin 2>/dev/null || true
+ sudo systemctl disable jellyfin 2>/dev/null || true
+ sudo systemctl mask jellyfin 2>/dev/null || true
+
+ echo '--> Opruimen van cache...'
+ sudo rm -rf /var/cache/jellyfin
+
+ echo '--> Opslagruimte controleren...'
+ df -h / /datadisk /storage
+"
+
+echo ""
+log_info "De oude standalone jellyfin host service is uitgeschakeld en gemaskeerd!"
+echo "De Jellyfin media server draait nu volledig in het Kubernetes cluster."
diff --git a/k8s/jellyfin/deployment.yaml b/k8s/jellyfin/deployment.yaml
new file mode 100644
index 0000000..f1f2132
--- /dev/null
+++ b/k8s/jellyfin/deployment.yaml
@@ -0,0 +1,133 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: jellyfin
+ namespace: htpc
+ labels:
+ app.kubernetes.io/name: jellyfin
+ app.kubernetes.io/part-of: htpc
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ app: jellyfin
+ template:
+ metadata:
+ labels:
+ app: jellyfin
+ app.kubernetes.io/name: jellyfin
+ app.kubernetes.io/part-of: htpc
+ spec:
+ nodeSelector:
+ kubernetes.io/hostname: vmserver
+ initContainers:
+ - name: migrate-config
+ image: lscr.io/linuxserver/jellyfin:latest
+ imagePullPolicy: IfNotPresent
+ command:
+ - /bin/bash
+ - -c
+ - |
+ if [ ! -f /config/system.xml ] && [ -f /host-etc/system.xml ]; then
+ echo "Syncing Jellyfin configuration from /etc/jellyfin..."
+ cp -a /host-etc/* /config/
+ sed -i 's|/var/lib/jellyfin/metadata|/config/metadata|g' /config/system.xml || true
+ sed -i 's|/var/cache/jellyfin|/config/cache|g' /config/system.xml || true
+ fi
+ if [ ! -f /config/data/jellyfin.db ] && [ -d /host-data/data ]; then
+ echo "Syncing Jellyfin data, metadata, plugins and root from /var/lib/jellyfin..."
+ mkdir -p /config/data /config/metadata /config/plugins /config/root
+ [ -d /host-data/data ] && cp -a /host-data/data/* /config/data/ || true
+ [ -d /host-data/metadata ] && cp -a /host-data/metadata/* /config/metadata/ || true
+ [ -d /host-data/plugins ] && cp -a /host-data/plugins/* /config/plugins/ || true
+ [ -d /host-data/root ] && cp -a /host-data/root/* /config/root/ || true
+ fi
+ chown -R 33:33 /config
+ volumeMounts:
+ - name: config
+ mountPath: /config
+ - name: host-etc
+ mountPath: /host-etc
+ readOnly: true
+ - name: host-data
+ mountPath: /host-data
+ readOnly: true
+ containers:
+ - name: jellyfin
+ image: lscr.io/linuxserver/jellyfin:latest
+ imagePullPolicy: IfNotPresent
+ securityContext:
+ privileged: true
+ env:
+ - name: PUID
+ value: "33"
+ - name: PGID
+ value: "33"
+ - name: TZ
+ value: "Europe/Amsterdam"
+ - name: DOTNET_gcServer
+ value: "0"
+ - name: DOTNET_GCHeapHardLimit
+ value: "0x160000000"
+ - name: DOTNET_GCLargeObjectHeapCompactionMode
+ value: "1"
+ ports:
+ - name: http
+ containerPort: 8096
+ hostPort: 8096
+ resources:
+ requests:
+ cpu: 200m
+ memory: 1Gi
+ limits:
+ cpu: 4000m
+ memory: 8Gi
+ volumeMounts:
+ - name: config
+ mountPath: /config
+ - name: datadisk
+ mountPath: /datadisk
+ - name: mnt-mp3
+ mountPath: /mnt/mp3
+ - name: dri
+ mountPath: /dev/dri
+ livenessProbe:
+ httpGet:
+ path: /jellyfin/health
+ port: 8096
+ initialDelaySeconds: 60
+ periodSeconds: 20
+ timeoutSeconds: 5
+ readinessProbe:
+ httpGet:
+ path: /jellyfin/health
+ port: 8096
+ initialDelaySeconds: 30
+ periodSeconds: 10
+ timeoutSeconds: 5
+ volumes:
+ - name: config
+ persistentVolumeClaim:
+ claimName: jellyfin-config
+ - name: datadisk
+ hostPath:
+ path: /datadisk
+ type: Directory
+ - name: mnt-mp3
+ hostPath:
+ path: /mnt/mp3
+ type: Directory
+ - name: dri
+ hostPath:
+ path: /dev/dri
+ type: Directory
+ - name: host-etc
+ hostPath:
+ path: /etc/jellyfin
+ type: DirectoryOrCreate
+ - name: host-data
+ hostPath:
+ path: /var/lib/jellyfin
+ type: DirectoryOrCreate
diff --git a/k8s/jellyfin/kustomization.yaml b/k8s/jellyfin/kustomization.yaml
new file mode 100644
index 0000000..48bc1a4
--- /dev/null
+++ b/k8s/jellyfin/kustomization.yaml
@@ -0,0 +1,7 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - pvc.yaml
+ - service.yaml
+ - deployment.yaml
diff --git a/k8s/jellyfin/pvc.yaml b/k8s/jellyfin/pvc.yaml
new file mode 100644
index 0000000..1227931
--- /dev/null
+++ b/k8s/jellyfin/pvc.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: jellyfin-config
+ namespace: htpc
+ labels:
+ app.kubernetes.io/name: jellyfin
+ app.kubernetes.io/part-of: htpc
+spec:
+ accessModes:
+ - ReadWriteOnce
+ storageClassName: local-path
+ resources:
+ requests:
+ storage: 30Gi
diff --git a/k8s/jellyfin/service.yaml b/k8s/jellyfin/service.yaml
new file mode 100644
index 0000000..211ae48
--- /dev/null
+++ b/k8s/jellyfin/service.yaml
@@ -0,0 +1,17 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: jellyfin
+ namespace: htpc
+ labels:
+ app.kubernetes.io/name: jellyfin
+ app.kubernetes.io/part-of: htpc
+spec:
+ type: ClusterIP
+ ports:
+ - name: http
+ port: 8096
+ targetPort: 8096
+ protocol: TCP
+ selector:
+ app: jellyfin
diff --git a/k8s/kustomization.yaml b/k8s/kustomization.yaml
index e9b9029..0c07799 100644
--- a/k8s/kustomization.yaml
+++ b/k8s/kustomization.yaml
@@ -12,5 +12,6 @@ resources:
- slskd
- jackett
- flaresolverr
+ - jellyfin
diff --git a/migrate_jellyfin_to_cluster.sh b/migrate_jellyfin_to_cluster.sh
new file mode 100755
index 0000000..5308b0c
--- /dev/null
+++ b/migrate_jellyfin_to_cluster.sh
@@ -0,0 +1,119 @@
+#!/usr/bin/env bash
+# ==============================================================================
+# Jellyfin Migration Script: Host Service -> Kubernetes Cluster (htpc namespace)
+# ==============================================================================
+set -euo pipefail
+
+CLUSTER_HOST="10.2.0.4"
+SSH_USER="bart"
+HTPC_NS="htpc"
+BACKUP_DIR="/storage/backups"
+DATE_STR=$(date +%Y-%m-%d)
+KUBECONFIG_PATH="${KUBECONFIG:-$HOME/.kube/config_vmserver}"
+
+COLOR_GREEN="\033[0;32m"
+COLOR_YELLOW="\033[1;33m"
+COLOR_RED="\033[0;31m"
+COLOR_BLUE="\033[0;34m"
+COLOR_RESET="\033[0m"
+
+log_info() { echo -e "${COLOR_GREEN}[INFO]${COLOR_RESET} $1"; }
+log_step() { echo -e "\n${COLOR_BLUE}==>${COLOR_RESET} ${COLOR_GREEN}$1${COLOR_RESET}"; }
+log_warn() { echo -e "${COLOR_YELLOW}[WARN]${COLOR_RESET} $1"; }
+log_err() { echo -e "${COLOR_RED}[ERROR]${COLOR_RESET} $1"; }
+
+echo "========================================================================"
+echo " JELLYFIN MIGRATIE NAAR KUBERNETES CLUSTER (NAMESPACE: ${HTPC_NS})"
+echo "========================================================================"
+echo "Bron: jellyfin.service op ${CLUSTER_HOST}"
+echo "Config: /etc/jellyfin & /var/lib/jellyfin"
+echo "Doel: K8s Pod in namespace '${HTPC_NS}' op ${CLUSTER_HOST}"
+echo "Poort: 8096 (ClusterIP & HostPort 8096)"
+echo "Transcode: AMD VA-API (/dev/dri/renderD128)"
+echo "========================================================================"
+echo ""
+
+read -p "Weet je zeker dat je de Jellyfin migratie naar Kubernetes wilt starten? (j/N): " -r CONFIRM
+if [[ ! "$CONFIRM" =~ ^[jJyY]$ ]]; then
+ log_info "Migratie geannuleerd."
+ exit 0
+fi
+
+# ------------------------------------------------------------------------------
+# STAP 1: PRE-FLIGHT CHECKS
+# ------------------------------------------------------------------------------
+log_step "1. Pre-flight checks uitvoeren..."
+
+log_info "Verbinding testen naar host (${CLUSTER_HOST})..."
+ssh -o BatchMode=yes -o ConnectTimeout=5 "${SSH_USER}@${CLUSTER_HOST}" "true" || {
+ log_err "Kan niet verbinden met host (${CLUSTER_HOST}) via SSH."
+ exit 1
+}
+
+log_info "Controleren van KUBECONFIG verbinding..."
+KUBECONFIG="${KUBECONFIG_PATH}" kubectl get nodes | grep -q "vmserver" || {
+ log_err "Kan vmserver node niet bereiken via kubectl (KUBECONFIG=${KUBECONFIG_PATH})!"
+ exit 1
+}
+
+# ------------------------------------------------------------------------------
+# STAP 2: RESERVEKOPIE MAKEN VAN JELLYFIN CONFIG & DATA
+# ------------------------------------------------------------------------------
+log_step "2. Reservekopie maken van /etc/jellyfin en /var/lib/jellyfin/data op ${CLUSTER_HOST}..."
+ssh -t "${SSH_USER}@${CLUSTER_HOST}" "
+ sudo mkdir -p '${BACKUP_DIR}'
+ if [ -d '/etc/jellyfin' ] || [ -d '/var/lib/jellyfin/data' ]; then
+ echo 'Inpakken van Jellyfin configuratie en database naar ${BACKUP_DIR}/jellyfin_backup_${DATE_STR}.tar.gz...'
+ sudo tar -czf '${BACKUP_DIR}/jellyfin_backup_${DATE_STR}.tar.gz' \
+ -C / etc/jellyfin \
+ -C /var/lib/jellyfin data
+ echo 'Backup gereed:'
+ ls -lh '${BACKUP_DIR}/jellyfin_backup_${DATE_STR}.tar.gz'
+ else
+ echo 'Geen bestaande Jellyfin data gevonden om te back-uppen.'
+ fi
+"
+
+# ------------------------------------------------------------------------------
+# STAP 3: STANDALONE JELLYFIN SERVICE STOPPEN OP DE HOST
+# ------------------------------------------------------------------------------
+log_step "3. Standalone jellyfin.service stoppen op ${CLUSTER_HOST}..."
+ssh -t "${SSH_USER}@${CLUSTER_HOST}" "
+ echo '--> Stoppen en uitschakelen van jellyfin.service...'
+ sudo systemctl stop jellyfin
+ sudo systemctl disable jellyfin
+"
+
+# ------------------------------------------------------------------------------
+# STAP 4: KUBERNETES MANIFESTS TOEPASSEN
+# ------------------------------------------------------------------------------
+log_step "4. Kubernetes manifests toepassen..."
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+KUBECONFIG="${KUBECONFIG_PATH}" kubectl apply -k "${SCRIPT_DIR}/k8s/"
+
+log_info "Wachten op succesvolle uitrol van Jellyfin pod (eerste start kan enkele minuten duren voor datamigratie)..."
+KUBECONFIG="${KUBECONFIG_PATH}" kubectl rollout status deployment/jellyfin -n "${HTPC_NS}" --timeout=300s
+
+# ------------------------------------------------------------------------------
+# STAP 5: VERIFICATIE
+# ------------------------------------------------------------------------------
+log_step "5. Verificatie van endpoints..."
+sleep 5
+
+log_info "Health endpoint testen via host (poort 8096)..."
+ssh -o BatchMode=yes "${SSH_USER}@${CLUSTER_HOST}" "curl -I -s http://127.0.0.1:8096/jellyfin/health" | head -n 1
+
+log_info "Cluster IP service testen..."
+CLUSTER_IP=$(KUBECONFIG="${KUBECONFIG_PATH}" kubectl get svc jellyfin -n "${HTPC_NS}" -o jsonpath='{.spec.clusterIP}')
+ssh -o BatchMode=yes "${SSH_USER}@${CLUSTER_HOST}" "curl -I -s http://${CLUSTER_IP}:8096/jellyfin/health" | head -n 1
+
+log_info "Controleer of render apparaten (/dev/dri) toegankelijk zijn in container..."
+POD_NAME=$(KUBECONFIG="${KUBECONFIG_PATH}" kubectl get pod -n "${HTPC_NS}" -l app=jellyfin -o jsonpath='{.items[0].metadata.name}')
+KUBECONFIG="${KUBECONFIG_PATH}" kubectl exec -n "${HTPC_NS}" "${POD_NAME}" -- ls -la /dev/dri || true
+
+echo ""
+echo "========================================================================"
+log_info "JELLYFIN MIGRATIE SUCCESVOL AFGEROND!"
+echo "De Jellyfin media server draait nu in Kubernetes."
+echo "Apache proxy: https://thuus.bnhmeijer.nl/jellyfin"
+echo "========================================================================"
diff --git a/rollback_jellyfin_migration.sh b/rollback_jellyfin_migration.sh
new file mode 100755
index 0000000..4aeed8d
--- /dev/null
+++ b/rollback_jellyfin_migration.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+# ==============================================================================
+# Jellyfin Rollback Script: Kubernetes -> Host Standalone jellyfin.service
+# ==============================================================================
+set -euo pipefail
+
+CLUSTER_HOST="10.2.0.4"
+SSH_USER="bart"
+HTPC_NS="htpc"
+KUBECONFIG_PATH="${KUBECONFIG:-$HOME/.kube/config_vmserver}"
+
+COLOR_GREEN="\033[0;32m"
+COLOR_YELLOW="\033[1;33m"
+COLOR_RED="\033[0;31m"
+COLOR_BLUE="\033[0;34m"
+COLOR_RESET="\033[0m"
+
+log_info() { echo -e "${COLOR_GREEN}[INFO]${COLOR_RESET} $1"; }
+log_step() { echo -e "\n${COLOR_BLUE}==>${COLOR_RESET} ${COLOR_GREEN}$1${COLOR_RESET}"; }
+log_warn() { echo -e "${COLOR_YELLOW}[WARN]${COLOR_RESET} $1"; }
+
+echo "========================================================================"
+echo " JELLYFIN ROLLBACK NAAR STANDALONE SERVICE OP $CLUSTER_HOST"
+echo "========================================================================"
+read -p "Weet je zeker dat je wilt terugrollen naar standalone jellyfin? (j/N): " -r CONFIRM
+if [[ ! "$CONFIRM" =~ ^[jJyY]$ ]]; then
+ log_info "Rollback geannuleerd."
+ exit 0
+fi
+
+log_step "1. Kubernetes Jellyfin deployment schalen naar 0..."
+KUBECONFIG="${KUBECONFIG_PATH}" kubectl scale deployment/jellyfin -n "${HTPC_NS}" --replicas=0 || true
+
+log_step "2. Standalone jellyfin.service inschakelen en starten op $CLUSTER_HOST..."
+ssh -t "${SSH_USER}@${CLUSTER_HOST}" "sudo systemctl enable --now jellyfin"
+
+log_step "3. Status controleren..."
+sleep 5
+ssh -o BatchMode=yes "${SSH_USER}@${CLUSTER_HOST}" "systemctl status jellyfin --no-pager" | head -n 12
+ssh -o BatchMode=yes "${SSH_USER}@${CLUSTER_HOST}" "curl -I -s http://127.0.0.1:8096/jellyfin/health" | head -n 1
+
+echo ""
+log_info "Rollback voltooid. Standalone jellyfin.service is weer actief op de host."