See full diagram gallery for interactive versions
Jellyfin runs as a 2-replica StatefulSet using the jellyfin-ha fork image, backed by a shared PostgreSQL database and Redis session cache. This enables concurrent access from multiple clients without session conflicts.
| Public URL | https://jellyfin.k3s.strommen.systems |
| Namespace | media |
| Image | harbor.k3s.internal.strommen.systems/production/jellyfin-ha:latest |
| Auth | Authentik SSO (web UI via jellyfin-plugin-sso) + Jellyfin native accounts (TV/mobile apps) |
| Replicas | 2 (StatefulSet, RollingUpdate) |
Image tag policy exception:
jellyfin-ha:latestis intentional — the internal fork CI pusheslatestonly. SHA-based tagging not yet adopted for this fork.
| Component | Image | Purpose |
|---|---|---|
jellyfin (x2) |
production/jellyfin-ha:latest |
Media server replicas (StatefulSet) |
jellyfin-postgres |
postgres:16.13-alpine |
Metadata + library database (5Gi Longhorn PVC) |
jellyfin-redis |
redis:7.4.2-alpine3.21 |
Session cache — no persistence (reconstructable) |
jellyfin-cache |
Service | Stable service alias for load balancing both replicas |
| PodDisruptionBudget | — | Ensures at least 1 replica during disruptions |
Valkey migration staged:
jellyfin-valkey.yamlexists as a staged Redis replacement but is not applied by default.
The jellyfin-ha fork adds a Jellyfin-PostgreSQL plugin. An init container injects database.xml at startup with <LockingBehavior>NoLock</LockingBehavior> — required for multi-replica shared access.
Env var note: The manifest uses
POSTGRES_CONNECTION_STRING. Jellyfin reads it internally asJELLYFIN_PostgreSQLConnectionString.
preferredDuringScheduling for gpu: intel-uhd-630 → targets k3s-agent-4podAntiAffinity preferred spread → tries to put replicas on different nodestolerations: gpu=true:NoSchedule → allows scheduling on GPU node| Volume | Mode | Mount | Purpose |
|---|---|---|---|
jellyfin-nfs-pvc |
NFS ROX (ReadOnlyMany) | /media |
Full media library from NAS |
jellyfin-transcode-rwx |
NFS RWX | /transcode |
Shared transcode scratch (50Gi) |
jellyfin-config-nfs |
NFS RWX | /config |
Config + metadata (shared by both replicas) |
UID/GID: 10010 / 10000 (svc-jellyfin / media-services NAS accounts).
Hardware transcode uses Intel Quick Sync Video (QSV) via VA-API on k3s-agent-4:
/dev/dri mounted as hostPathprivileged: true required for DRM ioctlssupplementalGroups: [44, 109, 991] for video/render groupsJellyfin web UI login uses Authentik via jellyfin-plugin-sso (OIDC), completed Phase 2.6 (2026-04-04).
app-media-streaming Authentik group members can complete SSO loginadmins group members receive Jellyfin admin privileges| Secret | Keys |
|---|---|
jellyfin-postgres-credentials |
POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, DATABASE_URL |
CronJob jellyfin-pg-backup — daily at 3:30 AM UTC → s3://k3s-homelab-backups-855878721457/postgres-backups/jellyfin/
kubernetes/apps/media/jellyfin-ha-dashboard.yamlJellyfinDown — fires if 0 replicas available for 5 min (kubernetes/apps/media/jellyfin-ha-alerts.yaml)/metrics endpointkubernetes/apps/media/jellyfin-networkpolicy.yaml enforces least-privilege:
| Policy | Direction | Allows |
|---|---|---|
| allow-jellyfin-ingress | Ingress → :8096 | Traefik (kube-system), Prometheus (monitoring + ipBlock 192.168.20.0/24), Jellyseerr (same namespace) |
| allow-jellyfin-egress | Egress | PostgreSQL (:5432), Redis (:6379), CoreDNS (:53), NAS NFS (ipBlock 192.168.30.0/24 :2049), Authentik MetalLB IP (:443), external HTTPS |
| allow-postgres-ingress | Ingress → :5432 | Jellyfin pods + pg-backup CronJob pod only |
| allow-redis-ingress | Ingress → :6379 | Jellyfin pods only |
kubernetes/apps/media/
jellyfin.yaml — StatefulSet (2 replicas), Services, IngressRoute
jellyfin-postgres.yaml — PostgreSQL StatefulSet + backup CronJob
jellyfin-redis.yaml — Redis session cache (production)
jellyfin-valkey.yaml — Valkey (staged replacement for Redis, not yet active)
jellyfin-cache-service.yaml — Stable cache service for load balancing
jellyfin-config-nfs.yaml — NFS PV/PVC for shared config
jellyfin-transcode-rwx.yaml — NFS PV/PVC for shared transcode scratch
jellyfin-networkpolicy.yaml — Network isolation
jellyfin-pdb.yaml — PodDisruptionBudget
jellyfin-pg-backup.yaml — Nightly backup CronJob (3:30 AM UTC)
jellyfin-ha-dashboard.yaml — Grafana dashboard ConfigMap
jellyfin-ha-alerts.yaml — PrometheusRules