feat: add Helm chart for jellyfin-ha (#3)
Adds a production-ready Helm chart under deploy/helm/jellyfin-ha/. Motivated by a community request on Reddit: https://www.reddit.com/r/JellyfinCommunity/comments/1rvj17f/jellyfin_ha_on_kubernetes_redisbacked_transcode/oav7mlz/ Features: - StatefulSet with configurable replica count (default 2 for HA) - Redis subchart (in-cluster) wired to ITranscodeSessionStore via Jellyfin__TranscodeStore__RedisConnectionString env var - Supports external Redis via ha.transcodeStore.existingSecret or ha.transcodeStore.redisConnectionString - Optional in-cluster PostgreSQL StatefulSet (experimental, mirrors existing kubernetes/apps/media/jellyfin-postgres.yaml pattern) - RWX config + transcode PVCs (required for multi-pod session takeover) - Per-pod cache via volumeClaimTemplates (RWO) - Optional NFS PV+PVC for media library - Intel QSV / VA-API GPUgit checkout -b feat/helm-chart && git add deploy/ && legit add deploy/ && git commit -m dagit commit -m featss
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: jellyfin-ha
|
||||||
|
description: >
|
||||||
|
High-availability Jellyfin media server with Redis-backed transcode session
|
||||||
|
store, lease-aware segment cleanup, and optional PostgreSQL database provider
|
||||||
|
for multi-pod Kubernetes deployments.
|
||||||
|
type: application
|
||||||
|
version: 0.1.0
|
||||||
|
appVersion: "10.12.0"
|
||||||
|
keywords:
|
||||||
|
- jellyfin
|
||||||
|
- media-server
|
||||||
|
- high-availability
|
||||||
|
- redis
|
||||||
|
- kubernetes
|
||||||
|
home: https://github.com/ZoltyMat/jellyfin-ha
|
||||||
|
sources:
|
||||||
|
- https://github.com/ZoltyMat/jellyfin-ha
|
||||||
|
maintainers:
|
||||||
|
- name: ZoltyMat
|
||||||
|
url: https://github.com/ZoltyMat
|
||||||
|
icon: https://raw.githubusercontent.com/jellyfin/jellyfin/master/Jellyfin.Server/Resources/Images/jellyfin-icon-solid.png
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
1. Jellyfin HA has been deployed.
|
||||||
|
|
||||||
|
{{- if eq (int .Values.replicaCount) 1 }}
|
||||||
|
⚠ replicaCount=1 — running in single-instance mode. Set replicaCount >= 2 and
|
||||||
|
ha.enabled=true to enable HA transcoding.
|
||||||
|
{{- else }}
|
||||||
|
✔ Running {{ .Values.replicaCount }} replicas.
|
||||||
|
{{- if include "jellyfin-ha.haEnabled" . }}
|
||||||
|
✔ HA mode: ACTIVE — transcode sessions replicated via Redis.
|
||||||
|
{{- else }}
|
||||||
|
⚠ HA mode: INACTIVE — NullTranscodeSessionStore in use.
|
||||||
|
Set redis.enabled=true (or ha.transcodeStore.redisConnectionString) to enable HA.
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
2. Get the Jellyfin URL:
|
||||||
|
|
||||||
|
{{- if .Values.ingress.enabled }}
|
||||||
|
{{- range .Values.ingress.hosts }}
|
||||||
|
https://{{ .host }}/
|
||||||
|
{{- end }}
|
||||||
|
{{- else if .Values.traefikIngressRoute.enabled }}
|
||||||
|
https://{{ .Values.traefikIngressRoute.host }}/
|
||||||
|
{{- else }}
|
||||||
|
Access via port-forward:
|
||||||
|
kubectl port-forward -n {{ .Release.Namespace }} svc/{{ include "jellyfin-ha.fullname" . }} 8096:{{ .Values.service.port }}
|
||||||
|
http://localhost:8096/
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
3. PostgreSQL:
|
||||||
|
{{- if .Values.postgresql.enabled }}
|
||||||
|
✔ In-cluster PostgreSQL deployed. Ensure the secret "{{ .Values.postgresql.existingSecret }}"
|
||||||
|
exists in namespace {{ .Release.Namespace }} before starting the server.
|
||||||
|
{{- else if eq .Values.config.databaseType "Jellyfin-PostgreSQL" }}
|
||||||
|
⚠ config.databaseType=Jellyfin-PostgreSQL but postgresql.enabled=false.
|
||||||
|
Make sure you have an external PostgreSQL and the correct DATABASE_URL env var set.
|
||||||
|
{{- else }}
|
||||||
|
Using SQLite (default). Enable postgresql.enabled=true for a shared database backend.
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
4. Transcode storage:
|
||||||
|
The transcode PVC must be ReadWriteMany when replicaCount > 1.
|
||||||
|
Current accessMode: {{ .Values.persistence.transcode.accessMode }}
|
||||||
|
{{- if and (gt (int .Values.replicaCount) 1) (ne .Values.persistence.transcode.accessMode "ReadWriteMany") }}
|
||||||
|
|
||||||
|
⚠ WARNING: replicaCount > 1 but transcode accessMode is not ReadWriteMany.
|
||||||
|
Pod B cannot read Pod A's HLS segments during session takeover.
|
||||||
|
Set persistence.transcode.accessMode=ReadWriteMany or use an NFS / Longhorn RWX PVC.
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
{{/*
|
||||||
|
Expand the name of the chart.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.name" -}}
|
||||||
|
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create a default fully qualified app name.
|
||||||
|
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||||
|
If release name contains chart name it will be used as a full name.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.fullname" -}}
|
||||||
|
{{- if .Values.fullnameOverride }}
|
||||||
|
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||||
|
{{- if contains $name .Release.Name }}
|
||||||
|
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create chart label.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.chart" -}}
|
||||||
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Common labels.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.labels" -}}
|
||||||
|
helm.sh/chart: {{ include "jellyfin-ha.chart" . }}
|
||||||
|
{{ include "jellyfin-ha.selectorLabels" . }}
|
||||||
|
{{- if .Chart.AppVersion }}
|
||||||
|
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||||
|
{{- end }}
|
||||||
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Selector labels.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.selectorLabels" -}}
|
||||||
|
app.kubernetes.io/name: {{ include "jellyfin-ha.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
app.kubernetes.io/component: server
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Service account name.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.serviceAccountName" -}}
|
||||||
|
{{- if .Values.serviceAccount.create }}
|
||||||
|
{{- default (include "jellyfin-ha.fullname" .) .Values.serviceAccount.name }}
|
||||||
|
{{- else }}
|
||||||
|
{{- default "default" .Values.serviceAccount.name }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Fully qualified name of the in-cluster Redis service.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.redis.fullname" -}}
|
||||||
|
{{- printf "%s-redis" (include "jellyfin-ha.fullname" .) }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Fully qualified name of the in-cluster PostgreSQL service.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.postgres.fullname" -}}
|
||||||
|
{{- printf "%s-postgres" (include "jellyfin-ha.fullname" .) }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Compute the Redis connection string.
|
||||||
|
Priority:
|
||||||
|
1. existingSecret (mounted as env var in the statefulset template)
|
||||||
|
2. explicit ha.transcodeStore.redisConnectionString value
|
||||||
|
3. auto-compose from the in-cluster Redis service name when redis.enabled=true
|
||||||
|
Returns empty string if none of the above apply (= single-instance / NullStore mode).
|
||||||
|
This helper returns the literal string only for cases 2 and 3; case 1 is handled
|
||||||
|
directly in the container env block via secretKeyRef.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.redisConnectionString" -}}
|
||||||
|
{{- if .Values.ha.transcodeStore.redisConnectionString }}
|
||||||
|
{{- .Values.ha.transcodeStore.redisConnectionString }}
|
||||||
|
{{- else if .Values.redis.enabled }}
|
||||||
|
{{- printf "%s:6379,abortConnect=false" (include "jellyfin-ha.redis.fullname" .) }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Return true if HA mode is active and Redis should be wired up.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.haEnabled" -}}
|
||||||
|
{{- if and .Values.ha.enabled (or .Values.redis.enabled .Values.ha.transcodeStore.redisConnectionString .Values.ha.transcodeStore.existingSecret) }}
|
||||||
|
{{- "true" }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Config PVC claim name — either the existing claim or the chart-managed one.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.configPvcName" -}}
|
||||||
|
{{- if .Values.persistence.config.existingClaim }}
|
||||||
|
{{- .Values.persistence.config.existingClaim }}
|
||||||
|
{{- else }}
|
||||||
|
{{- printf "%s-config" (include "jellyfin-ha.fullname" .) }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Transcode PVC claim name — either the existing claim or the chart-managed one.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.transcodePvcName" -}}
|
||||||
|
{{- if .Values.persistence.transcode.existingClaim }}
|
||||||
|
{{- .Values.persistence.transcode.existingClaim }}
|
||||||
|
{{- else }}
|
||||||
|
{{- printf "%s-transcode" (include "jellyfin-ha.fullname" .) }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Media PVC claim name — either the existing claim or the chart-managed NFS PVC.
|
||||||
|
*/}}
|
||||||
|
{{- define "jellyfin-ha.mediaPvcName" -}}
|
||||||
|
{{- if .Values.persistence.media.existingClaim }}
|
||||||
|
{{- .Values.persistence.media.existingClaim }}
|
||||||
|
{{- else if .Values.persistence.media.nfs.enabled }}
|
||||||
|
{{- printf "%s-media" (include "jellyfin-ha.fullname" .) }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{{- if .Values.runtimeConfig.enabled }}
|
||||||
|
# jellyfin.runtimeconfig.json ConfigMap.
|
||||||
|
# Mount path: /jellyfin/jellyfin.runtimeconfig.json
|
||||||
|
# Use this to set .NET runtime configuration switches (e.g. Intel QSV codec flags).
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}-runtimeconfig
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
data:
|
||||||
|
jellyfin.runtimeconfig.json: |
|
||||||
|
{{- .Values.runtimeConfig.json | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
{{- if .Values.ingress.enabled }}
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.ingress.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
{{- if .Values.ingress.className }}
|
||||||
|
ingressClassName: {{ .Values.ingress.className }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.ingress.tls }}
|
||||||
|
tls:
|
||||||
|
{{- toYaml .Values.ingress.tls | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
rules:
|
||||||
|
{{- range .Values.ingress.hosts }}
|
||||||
|
- host: {{ .host | quote }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
{{- range .paths }}
|
||||||
|
- path: {{ .path }}
|
||||||
|
pathType: {{ .pathType }}
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" $ }}
|
||||||
|
port:
|
||||||
|
name: http
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
---
|
||||||
|
{{- if .Values.traefikIngressRoute.enabled }}
|
||||||
|
# Traefik v3 IngressRoute (used by k3s default ingress controller).
|
||||||
|
# Enables sticky session cookies — required for multi-replica Jellyfin so that
|
||||||
|
# a client always lands on the same pod (session affinity).
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
{{- if .Values.traefikIngressRoute.tls.enabled }}
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/cluster-issuer: {{ .Values.traefikIngressRoute.tls.clusterIssuer }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
{{- toYaml .Values.traefikIngressRoute.entryPoints | nindent 4 }}
|
||||||
|
routes:
|
||||||
|
- match: Host(`{{ .Values.traefikIngressRoute.host }}`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: {{ include "jellyfin-ha.fullname" . }}
|
||||||
|
port: {{ .Values.service.port }}
|
||||||
|
{{- if .Values.traefikIngressRoute.sticky.enabled }}
|
||||||
|
sticky:
|
||||||
|
cookie:
|
||||||
|
name: {{ .Values.traefikIngressRoute.sticky.cookieName }}
|
||||||
|
httpOnly: {{ .Values.traefikIngressRoute.sticky.httpOnly }}
|
||||||
|
secure: {{ .Values.traefikIngressRoute.sticky.secure }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.traefikIngressRoute.tls.enabled }}
|
||||||
|
tls:
|
||||||
|
secretName: {{ .Values.traefikIngressRoute.tls.secretName }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
---
|
||||||
|
{{- if .Values.traefikIngressRoute.tls.enabled }}
|
||||||
|
# cert-manager Certificate for Traefik TLS termination.
|
||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}-tls
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
secretName: {{ .Values.traefikIngressRoute.tls.secretName }}
|
||||||
|
issuerRef:
|
||||||
|
name: {{ .Values.traefikIngressRoute.tls.clusterIssuer }}
|
||||||
|
kind: ClusterIssuer
|
||||||
|
dnsNames:
|
||||||
|
{{- if .Values.traefikIngressRoute.tls.dnsNames }}
|
||||||
|
{{- toYaml .Values.traefikIngressRoute.tls.dnsNames | nindent 4 }}
|
||||||
|
{{- else }}
|
||||||
|
- {{ .Values.traefikIngressRoute.host }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{{- if .Values.podDisruptionBudget.enabled }}
|
||||||
|
apiVersion: policy/v1
|
||||||
|
kind: PodDisruptionBudget
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "jellyfin-ha.selectorLabels" . | nindent 6 }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{{- if .Values.postgresql.enabled }}
|
||||||
|
# PersistentVolumeClaim for the in-cluster PostgreSQL data directory.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.postgres.fullname" . }}-data
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: database
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
{{- if .Values.postgresql.persistence.storageClass }}
|
||||||
|
storageClassName: {{ .Values.postgresql.persistence.storageClass | quote }}
|
||||||
|
{{- end }}
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .Values.postgresql.persistence.size }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{{- if .Values.postgresql.enabled }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.postgres.fullname" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: database
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: {{ include "jellyfin-ha.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
app.kubernetes.io/component: database
|
||||||
|
ports:
|
||||||
|
- name: postgres
|
||||||
|
port: {{ .Values.postgresql.service.port }}
|
||||||
|
targetPort: postgres
|
||||||
|
protocol: TCP
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
{{- if .Values.postgresql.enabled }}
|
||||||
|
# In-cluster PostgreSQL StatefulSet — experimental.
|
||||||
|
# The credentials secret must be created manually before first deploy:
|
||||||
|
#
|
||||||
|
# kubectl create secret generic {{ .Values.postgresql.existingSecret }} \
|
||||||
|
# --namespace {{ .Release.Namespace }} \
|
||||||
|
# --from-literal=POSTGRES_USER=jellyfin \
|
||||||
|
# --from-literal=POSTGRES_PASSWORD=<strong-password> \
|
||||||
|
# --from-literal=POSTGRES_DB=jellyfin \
|
||||||
|
# --from-literal=DATABASE_URL="postgresql://jellyfin:<password>@{{ include "jellyfin-ha.postgres.fullname" . }}:5432/jellyfin"
|
||||||
|
#
|
||||||
|
# SECURITY: Do NOT add a Secret resource here. Applying this file must not
|
||||||
|
# overwrite a live secret.
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.postgres.fullname" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: database
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
serviceName: {{ include "jellyfin-ha.postgres.fullname" . }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: {{ include "jellyfin-ha.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
app.kubernetes.io/component: database
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: {{ include "jellyfin-ha.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
app.kubernetes.io/component: database
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: postgres
|
||||||
|
image: "{{ .Values.postgresql.image.repository }}:{{ .Values.postgresql.image.tag }}"
|
||||||
|
imagePullPolicy: {{ .Values.postgresql.image.pullPolicy }}
|
||||||
|
ports:
|
||||||
|
- name: postgres
|
||||||
|
containerPort: 5432
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
- name: POSTGRES_USER
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ .Values.postgresql.existingSecret }}
|
||||||
|
key: POSTGRES_USER
|
||||||
|
- name: POSTGRES_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ .Values.postgresql.existingSecret }}
|
||||||
|
key: POSTGRES_PASSWORD
|
||||||
|
- name: POSTGRES_DB
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ .Values.postgresql.existingSecret }}
|
||||||
|
key: POSTGRES_DB
|
||||||
|
- name: PGDATA
|
||||||
|
value: /var/lib/postgresql/data/pgdata
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.postgresql.resources | nindent 12 }}
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["pg_isready", "-U", "$(POSTGRES_USER)"]
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 20
|
||||||
|
timeoutSeconds: 5
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["pg_isready", "-U", "$(POSTGRES_USER)"]
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 3
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /var/lib/postgresql/data
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: {{ include "jellyfin-ha.postgres.fullname" . }}-data
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
{{- if not .Values.persistence.config.existingClaim }}
|
||||||
|
# Shared config PVC — used by all Jellyfin replicas.
|
||||||
|
# Must be ReadWriteMany when replicaCount > 1.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}-config
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: storage
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- {{ .Values.persistence.config.accessMode }}
|
||||||
|
{{- if .Values.persistence.config.storageClass }}
|
||||||
|
storageClassName: {{ .Values.persistence.config.storageClass | quote }}
|
||||||
|
{{- end }}
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .Values.persistence.config.size }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
---
|
||||||
|
{{- if not .Values.persistence.transcode.existingClaim }}
|
||||||
|
# Shared transcode PVC — must be ReadWriteMany so pod takeover can read
|
||||||
|
# HLS segments written by the previous owner pod.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}-transcode
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: storage
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- {{ .Values.persistence.transcode.accessMode }}
|
||||||
|
{{- if .Values.persistence.transcode.storageClass }}
|
||||||
|
storageClassName: {{ .Values.persistence.transcode.storageClass | quote }}
|
||||||
|
{{- end }}
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .Values.persistence.transcode.size }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
---
|
||||||
|
{{- if and .Values.persistence.media.nfs.enabled (not .Values.persistence.media.existingClaim) }}
|
||||||
|
# NFS PersistentVolume and PersistentVolumeClaim for the media library.
|
||||||
|
# Enable persistence.media.nfs.enabled and provide server/path to use this.
|
||||||
|
# Alternatively, set persistence.media.existingClaim to reuse an existing PVC.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}-media-nfs
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: storage
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: {{ .Values.persistence.media.nfs.size }}
|
||||||
|
accessModes:
|
||||||
|
- ReadOnlyMany
|
||||||
|
persistentVolumeReclaimPolicy: Retain
|
||||||
|
{{- if .Values.persistence.media.nfs.storageClass }}
|
||||||
|
storageClassName: {{ .Values.persistence.media.nfs.storageClass | quote }}
|
||||||
|
{{- end }}
|
||||||
|
nfs:
|
||||||
|
server: {{ .Values.persistence.media.nfs.server | quote }}
|
||||||
|
path: {{ .Values.persistence.media.nfs.path | quote }}
|
||||||
|
readOnly: true
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}-media
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: storage
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadOnlyMany
|
||||||
|
{{- if .Values.persistence.media.nfs.storageClass }}
|
||||||
|
storageClassName: {{ .Values.persistence.media.nfs.storageClass | quote }}
|
||||||
|
{{- end }}
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .Values.persistence.media.nfs.size }}
|
||||||
|
volumeName: {{ include "jellyfin-ha.fullname" . }}-media-nfs
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{{- if .Values.redis.enabled }}
|
||||||
|
# ConfigMap holding the Redis configuration file.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.redis.fullname" . }}-config
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: redis
|
||||||
|
data:
|
||||||
|
redis.conf: |
|
||||||
|
maxmemory {{ .Values.redis.maxmemory }}
|
||||||
|
maxmemory-policy {{ .Values.redis.maxmemoryPolicy }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{{- if .Values.redis.enabled }}
|
||||||
|
# In-cluster Redis Deployment for jellyifn-ha transcode session store.
|
||||||
|
# No persistence — lease data is small and reconstructable on restart.
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.redis.fullname" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: redis
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: {{ include "jellyfin-ha.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
app.kubernetes.io/component: redis
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: {{ include "jellyfin-ha.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
app.kubernetes.io/component: redis
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: redis
|
||||||
|
image: "{{ .Values.redis.image.repository }}:{{ .Values.redis.image.tag }}"
|
||||||
|
imagePullPolicy: {{ .Values.redis.image.pullPolicy }}
|
||||||
|
args: ["redis-server", "/etc/redis/redis.conf"]
|
||||||
|
ports:
|
||||||
|
- name: redis
|
||||||
|
containerPort: 6379
|
||||||
|
protocol: TCP
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.redis.resources | nindent 12 }}
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["redis-cli", "ping"]
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
periodSeconds: 20
|
||||||
|
timeoutSeconds: 5
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["redis-cli", "ping"]
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 3
|
||||||
|
volumeMounts:
|
||||||
|
- name: config
|
||||||
|
mountPath: /etc/redis
|
||||||
|
volumes:
|
||||||
|
- name: config
|
||||||
|
configMap:
|
||||||
|
name: {{ include "jellyfin-ha.redis.fullname" . }}-config
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{{- if .Values.redis.enabled }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.redis.fullname" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: redis
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: {{ include "jellyfin-ha.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
app.kubernetes.io/component: redis
|
||||||
|
ports:
|
||||||
|
- name: redis
|
||||||
|
port: 6379
|
||||||
|
targetPort: redis
|
||||||
|
protocol: TCP
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.service.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.service.type }}
|
||||||
|
selector:
|
||||||
|
{{- include "jellyfin-ha.selectorLabels" . | nindent 4 }}
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: {{ .Values.service.port }}
|
||||||
|
targetPort: http
|
||||||
|
protocol: TCP
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{{- if .Values.serviceMonitor.enabled }}
|
||||||
|
# Prometheus ServiceMonitor.
|
||||||
|
# Jellyfin does not expose a native /metrics endpoint. Enable this if you have
|
||||||
|
# a Prometheus sidecar or plan to add one. The kube-state-metrics replica count
|
||||||
|
# alert is the primary health signal for Jellyfin without a native exporter.
|
||||||
|
apiVersion: monitoring.coreos.com/v1
|
||||||
|
kind: ServiceMonitor
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.serviceMonitor.additionalLabels }}
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "jellyfin-ha.selectorLabels" . | nindent 6 }}
|
||||||
|
endpoints:
|
||||||
|
- port: http
|
||||||
|
path: {{ .Values.serviceMonitor.path }}
|
||||||
|
interval: {{ .Values.serviceMonitor.interval }}
|
||||||
|
namespaceSelector:
|
||||||
|
matchNames:
|
||||||
|
- {{ .Release.Namespace }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.labels }}
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
replicas: {{ .Values.replicaCount }}
|
||||||
|
serviceName: {{ include "jellyfin-ha.fullname" . }}
|
||||||
|
updateStrategy:
|
||||||
|
{{- toYaml .Values.updateStrategy | nindent 4 }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "jellyfin-ha.selectorLabels" . | nindent 6 }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.selectorLabels" . | nindent 8 }}
|
||||||
|
{{- with .Values.podLabels }}
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.podAnnotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
serviceAccountName: {{ include "jellyfin-ha.serviceAccountName" . }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Affinity / anti-affinity
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
affinity:
|
||||||
|
{{- if and .Values.gpu.enabled .Values.gpu.intel.nodeLabel.key }}
|
||||||
|
nodeAffinity:
|
||||||
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- weight: 100
|
||||||
|
preference:
|
||||||
|
matchExpressions:
|
||||||
|
- key: {{ .Values.gpu.intel.nodeLabel.key }}
|
||||||
|
operator: In
|
||||||
|
values:
|
||||||
|
- {{ .Values.gpu.intel.nodeLabel.value }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.podAntiAffinity.enabled }}
|
||||||
|
podAntiAffinity:
|
||||||
|
{{- if eq .Values.podAntiAffinity.type "required" }}
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- labelSelector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "jellyfin-ha.selectorLabels" . | nindent 18 }}
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
{{- else }}
|
||||||
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- weight: {{ .Values.podAntiAffinity.weight }}
|
||||||
|
podAffinityTerm:
|
||||||
|
labelSelector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "jellyfin-ha.selectorLabels" . | nindent 20 }}
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
# GPU node toleration
|
||||||
|
{{- if .Values.gpu.enabled }}
|
||||||
|
tolerations:
|
||||||
|
- key: {{ .Values.gpu.intel.toleration.key }}
|
||||||
|
operator: Equal
|
||||||
|
value: {{ .Values.gpu.intel.toleration.value | quote }}
|
||||||
|
effect: {{ .Values.gpu.intel.toleration.effect }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Init containers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
initContainers:
|
||||||
|
{{- if eq .Values.config.databaseType "Jellyfin-PostgreSQL" }}
|
||||||
|
# Inject database.xml to select the PostgreSQL provider at startup.
|
||||||
|
- name: inject-db-config
|
||||||
|
image: busybox:1.37.0
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
mkdir -p /config/config
|
||||||
|
chown {{ .Values.securityContext.runAsUser }}:{{ .Values.securityContext.runAsGroup }} /config/config
|
||||||
|
chmod 775 /config/config
|
||||||
|
cat > /config/config/database.xml << 'DBEOF'
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<DatabaseConfigurationOptions>
|
||||||
|
<DatabaseType>Jellyfin-PostgreSQL</DatabaseType>
|
||||||
|
<LockingBehavior>NoLock</LockingBehavior>
|
||||||
|
</DatabaseConfigurationOptions>
|
||||||
|
DBEOF
|
||||||
|
chown {{ .Values.securityContext.runAsUser }}:{{ .Values.securityContext.runAsGroup }} /config/config/database.xml
|
||||||
|
chmod 664 /config/config/database.xml
|
||||||
|
echo "database.xml injected."
|
||||||
|
volumeMounts:
|
||||||
|
- name: config
|
||||||
|
mountPath: /config
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.extraInitContainers }}
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Main container
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
containers:
|
||||||
|
- name: jellyfin
|
||||||
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||||
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 8096
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
# Pod identity — used by the Redis transcode lease store to identify this replica.
|
||||||
|
- name: JELLYFIN_HA_POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
- name: JELLYFIN_INSTANCE_ID
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
|
||||||
|
# Disable UDP auto-discovery when running multiple replicas.
|
||||||
|
- name: JELLYFIN_Network__AutoDiscovery
|
||||||
|
value: {{ .Values.config.autoDiscovery | quote }}
|
||||||
|
|
||||||
|
# Config directory (must differ from data root; see Jellyfin sanity check).
|
||||||
|
- name: JELLYFIN_CONFIG_DIR
|
||||||
|
value: {{ .Values.config.configDir | quote }}
|
||||||
|
|
||||||
|
{{- if .Values.config.publishedServerUrl }}
|
||||||
|
- name: JELLYFIN_PublishedServerUrl
|
||||||
|
value: {{ .Values.config.publishedServerUrl | quote }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Redis (HA transcode session store)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
{{- if include "jellyfin-ha.haEnabled" . }}
|
||||||
|
{{- if .Values.ha.transcodeStore.existingSecret }}
|
||||||
|
# Connection string sourced from an existing secret.
|
||||||
|
- name: Jellyfin__TranscodeStore__RedisConnectionString
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ .Values.ha.transcodeStore.existingSecret }}
|
||||||
|
key: {{ .Values.ha.transcodeStore.existingSecretKey }}
|
||||||
|
{{- else }}
|
||||||
|
- name: Jellyfin__TranscodeStore__RedisConnectionString
|
||||||
|
value: {{ include "jellyfin-ha.redisConnectionString" . | quote }}
|
||||||
|
{{- end }}
|
||||||
|
- name: Jellyfin__TranscodeStore__LeaseDurationSeconds
|
||||||
|
value: {{ .Values.ha.transcodeStore.leaseDurationSeconds | quote }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# PostgreSQL (experimental)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
{{- if and .Values.postgresql.enabled (eq .Values.config.databaseType "Jellyfin-PostgreSQL") }}
|
||||||
|
- name: POSTGRES_CONNECTION_STRING
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ .Values.postgresql.existingSecret }}
|
||||||
|
key: DATABASE_URL
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Extra environment variables
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
{{- with .Values.config.extraEnv }}
|
||||||
|
{{- toYaml . | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.resources | nindent 12 }}
|
||||||
|
|
||||||
|
securityContext:
|
||||||
|
privileged: {{ if and .Values.gpu.enabled .Values.gpu.mountDri }}true{{ else }}{{ .Values.securityContext.privileged }}{{ end }}
|
||||||
|
runAsUser: {{ .Values.securityContext.runAsUser }}
|
||||||
|
runAsGroup: {{ .Values.securityContext.runAsGroup }}
|
||||||
|
|
||||||
|
volumeMounts:
|
||||||
|
- name: config
|
||||||
|
mountPath: /config
|
||||||
|
{{- if or .Values.persistence.media.existingClaim (and .Values.persistence.media.nfs.enabled) }}
|
||||||
|
- name: media
|
||||||
|
mountPath: /media
|
||||||
|
readOnly: true
|
||||||
|
{{- end }}
|
||||||
|
- name: transcode
|
||||||
|
mountPath: /config/transcodes
|
||||||
|
- name: cache
|
||||||
|
mountPath: /cache
|
||||||
|
{{- if and .Values.gpu.enabled .Values.gpu.mountDri }}
|
||||||
|
- name: dri
|
||||||
|
mountPath: /dev/dri
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.runtimeConfig.enabled }}
|
||||||
|
- name: runtimeconfig
|
||||||
|
mountPath: /jellyfin/jellyfin.runtimeconfig.json
|
||||||
|
subPath: jellyfin.runtimeconfig.json
|
||||||
|
readOnly: true
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.extraVolumeMounts }}
|
||||||
|
{{- toYaml . | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
livenessProbe:
|
||||||
|
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
||||||
|
readinessProbe:
|
||||||
|
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Volumes (static — shared across all pods)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
volumes:
|
||||||
|
- name: config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: {{ include "jellyfin-ha.configPvcName" . }}
|
||||||
|
- name: transcode
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: {{ include "jellyfin-ha.transcodePvcName" . }}
|
||||||
|
{{- if or .Values.persistence.media.existingClaim .Values.persistence.media.nfs.enabled }}
|
||||||
|
- name: media
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: {{ include "jellyfin-ha.mediaPvcName" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if and .Values.gpu.enabled .Values.gpu.mountDri }}
|
||||||
|
- name: dri
|
||||||
|
hostPath:
|
||||||
|
path: /dev/dri
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.runtimeConfig.enabled }}
|
||||||
|
- name: runtimeconfig
|
||||||
|
configMap:
|
||||||
|
name: {{ include "jellyfin-ha.fullname" . }}-runtimeconfig
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.extraVolumes }}
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Per-pod volumes via volumeClaimTemplates
|
||||||
|
# Cache is per-pod (RWO) — each replica has an independent transcoding cache,
|
||||||
|
# which avoids lock contention and is safe to lose on pod termination.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: cache
|
||||||
|
labels:
|
||||||
|
{{- include "jellyfin-ha.labels" . | nindent 10 }}
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
{{- if .Values.persistence.cache.storageClass }}
|
||||||
|
storageClassName: {{ .Values.persistence.cache.storageClass | quote }}
|
||||||
|
{{- end }}
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .Values.persistence.cache.size }}
|
||||||
@@ -0,0 +1,405 @@
|
|||||||
|
# Default values for jellyfin-ha.
|
||||||
|
# This is a YAML-formatted file.
|
||||||
|
|
||||||
|
# -- Override the chart name.
|
||||||
|
nameOverride: ""
|
||||||
|
# -- Override the full resource name prefix.
|
||||||
|
fullnameOverride: ""
|
||||||
|
|
||||||
|
# -- Number of Jellyfin replicas.
|
||||||
|
# Set >= 2 to use HA mode. When replicaCount > 1, ha.enabled should be true
|
||||||
|
# and a Redis connection must be configured (via redis.enabled or ha.transcodeStore.redisConnectionString).
|
||||||
|
replicaCount: 2
|
||||||
|
|
||||||
|
# -- Container image configuration.
|
||||||
|
image:
|
||||||
|
repository: "your-registry/jellyfin-ha"
|
||||||
|
tag: "latest"
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
# -- Image pull secrets (e.g. for private ECR registries).
|
||||||
|
# Example:
|
||||||
|
# - name: ecr-pull-secret
|
||||||
|
imagePullSecrets: []
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# HA (High-Availability) configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
ha:
|
||||||
|
# -- Enable HA mode. When true, a Redis connection string is required
|
||||||
|
# (either via redis.enabled or ha.transcodeStore.redisConnectionString).
|
||||||
|
# When false, NullTranscodeSessionStore is used and behavior is identical
|
||||||
|
# to upstream Jellyfin.
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
transcodeStore:
|
||||||
|
# -- StackExchange.Redis connection string.
|
||||||
|
# Leave empty to auto-compose from the in-cluster Redis service when redis.enabled=true.
|
||||||
|
# Explicit examples:
|
||||||
|
# redis:6379
|
||||||
|
# redis:6379,password=secret
|
||||||
|
# redis.example.com:6380,ssl=true,abortConnect=false
|
||||||
|
# sentinel-host:26379,serviceName=mymaster
|
||||||
|
redisConnectionString: ""
|
||||||
|
|
||||||
|
# -- How long (seconds) a pod's transcode lease is valid before another pod may take over.
|
||||||
|
leaseDurationSeconds: 30
|
||||||
|
|
||||||
|
# -- Secret containing the Redis connection string.
|
||||||
|
# If set, the connection string is read from this secret instead of the value above.
|
||||||
|
# The secret must have a key named by existingSecret.key.
|
||||||
|
existingSecret: ""
|
||||||
|
existingSecretKey: "connection-string"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# In-cluster Redis (for transcode session store)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
redis:
|
||||||
|
# -- Deploy an in-cluster Redis instance.
|
||||||
|
# Disable and set ha.transcodeStore.redisConnectionString to use an external Redis.
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
image:
|
||||||
|
repository: redis
|
||||||
|
tag: "7.4.2-alpine3.21"
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
# -- Maximum memory for Redis to use.
|
||||||
|
maxmemory: "256mb"
|
||||||
|
# -- LRU eviction policy when maxmemory is reached.
|
||||||
|
maxmemoryPolicy: "allkeys-lru"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 25m
|
||||||
|
memory: 64Mi
|
||||||
|
limits:
|
||||||
|
cpu: 200m
|
||||||
|
memory: 256Mi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Jellyfin application configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
config:
|
||||||
|
# -- The externally-reachable URL Jellyfin reports to clients.
|
||||||
|
publishedServerUrl: ""
|
||||||
|
|
||||||
|
# -- Disable UDP auto-discovery (port 7359).
|
||||||
|
# Recommended when running multiple replicas to prevent duplicate discovery responses.
|
||||||
|
autoDiscovery: false
|
||||||
|
|
||||||
|
# -- Jellyfin config directory inside the container.
|
||||||
|
# Must differ from the data/root directory to pass Jellyfin's sanity check.
|
||||||
|
configDir: "/config/config"
|
||||||
|
|
||||||
|
# -- Database provider: "SQLite" (default) or "Jellyfin-PostgreSQL" (experimental).
|
||||||
|
# When set to "Jellyfin-PostgreSQL", an init container will inject database.xml
|
||||||
|
# and the postgresql.enabled section (or an external connection string) must be configured.
|
||||||
|
databaseType: "SQLite"
|
||||||
|
|
||||||
|
# -- Extra environment variables to set on the Jellyfin container.
|
||||||
|
# Example:
|
||||||
|
# - name: JELLYFIN_Network__BaseUrl
|
||||||
|
# value: "/jellyfin"
|
||||||
|
extraEnv: []
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# PostgreSQL (experimental — only needed when config.databaseType = Jellyfin-PostgreSQL)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
postgresql:
|
||||||
|
# -- Deploy an in-cluster PostgreSQL instance.
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
image:
|
||||||
|
repository: postgres
|
||||||
|
tag: "16.6-alpine3.21"
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: "1"
|
||||||
|
memory: 1Gi
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
storageClass: ""
|
||||||
|
size: 5Gi
|
||||||
|
|
||||||
|
# -- Name of an existing secret with PostgreSQL credentials.
|
||||||
|
# Required when postgresql.enabled=true. The secret must contain:
|
||||||
|
# POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, DATABASE_URL
|
||||||
|
# Create it with:
|
||||||
|
# kubectl create secret generic jellyfin-postgres-credentials \
|
||||||
|
# --from-literal=POSTGRES_USER=jellyfin \
|
||||||
|
# --from-literal=POSTGRES_PASSWORD=<password> \
|
||||||
|
# --from-literal=POSTGRES_DB=jellyfin \
|
||||||
|
# --from-literal=DATABASE_URL="postgresql://jellyfin:<password>@<host>:5432/jellyfin"
|
||||||
|
existingSecret: "jellyfin-postgres-credentials"
|
||||||
|
|
||||||
|
service:
|
||||||
|
port: 5432
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# GPU / hardware transcoding
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
gpu:
|
||||||
|
# -- Enable Intel QSV / VA-API hardware transcoding.
|
||||||
|
# Mounts /dev/dri from the host and sets the required security context.
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
intel:
|
||||||
|
# -- Node affinity label to prefer GPU-capable nodes.
|
||||||
|
nodeLabel:
|
||||||
|
key: gpu
|
||||||
|
value: intel-uhd-630
|
||||||
|
|
||||||
|
# -- Toleration for the GPU node taint.
|
||||||
|
toleration:
|
||||||
|
key: gpu
|
||||||
|
value: "true"
|
||||||
|
effect: NoSchedule
|
||||||
|
|
||||||
|
# -- Mount /dev/dri from the host (required for VA-API; implies privileged=true).
|
||||||
|
mountDri: true
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Persistence
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
persistence:
|
||||||
|
# Config volume — single-writer; RWO is fine for single-replica deployments.
|
||||||
|
# For multi-replica: use an RWX storage class (e.g. Longhorn RWX, NFS) or
|
||||||
|
# point all pods at an existing shared PVC via existingClaim.
|
||||||
|
config:
|
||||||
|
# -- Size of the config PVC.
|
||||||
|
size: 5Gi
|
||||||
|
# -- Storage class. Leave empty to use the cluster default.
|
||||||
|
storageClass: ""
|
||||||
|
# -- Access mode. Use ReadWriteMany when replicaCount > 1 and sharing one PVC.
|
||||||
|
accessMode: ReadWriteMany
|
||||||
|
# -- Reuse an existing PVC. When set, no new PVC is created.
|
||||||
|
existingClaim: ""
|
||||||
|
|
||||||
|
# Media volume — read-only mount shared by all pods.
|
||||||
|
# Configure one of: existingClaim (for an existing PVC), nfs (to create an NFS PV+PVC),
|
||||||
|
# or existingClaim pointing at a pre-created PVC.
|
||||||
|
media:
|
||||||
|
# -- Reuse an existing media PVC (most common for homelab NFS/Longhorn setups).
|
||||||
|
existingClaim: ""
|
||||||
|
# -- Create an NFS-backed PV and PVC for the media library.
|
||||||
|
nfs:
|
||||||
|
enabled: false
|
||||||
|
server: "your-nas.local"
|
||||||
|
path: "/media"
|
||||||
|
size: 1Ti
|
||||||
|
storageClass: ""
|
||||||
|
|
||||||
|
# Transcode volume — MUST be ReadWriteMany when replicaCount > 1 so that
|
||||||
|
# a recovering pod can read HLS segments written by the pod it is replacing.
|
||||||
|
# When replicaCount=1, ReadWriteOnce is acceptable.
|
||||||
|
transcode:
|
||||||
|
size: 30Gi
|
||||||
|
storageClass: ""
|
||||||
|
accessMode: ReadWriteMany
|
||||||
|
existingClaim: ""
|
||||||
|
|
||||||
|
# Per-pod cache volume — local to each pod; always RWO.
|
||||||
|
# Created via StatefulSet volumeClaimTemplates (one PVC per pod).
|
||||||
|
cache:
|
||||||
|
size: 30Gi
|
||||||
|
storageClass: ""
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Service
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 8096
|
||||||
|
# -- Annotations for the Service resource.
|
||||||
|
annotations: {}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Ingress (standard Kubernetes Ingress)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
ingress:
|
||||||
|
enabled: false
|
||||||
|
# -- Ingress class name (e.g. "nginx", "traefik").
|
||||||
|
className: ""
|
||||||
|
annotations: {}
|
||||||
|
# cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||||
|
# nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||||
|
hosts:
|
||||||
|
- host: jellyfin.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls: []
|
||||||
|
# - secretName: jellyfin-tls
|
||||||
|
# hosts:
|
||||||
|
# - jellyfin.example.com
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Traefik IngressRoute (Traefik v3 CRD — used by k3s default ingress)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
traefikIngressRoute:
|
||||||
|
enabled: false
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
# -- Hostname for the Traefik routing rule.
|
||||||
|
host: "jellyfin.example.com"
|
||||||
|
# -- Enable sticky session cookie (recommended for multi-replica Jellyfin).
|
||||||
|
sticky:
|
||||||
|
enabled: true
|
||||||
|
cookieName: "jellyfin-server-id"
|
||||||
|
httpOnly: true
|
||||||
|
secure: true
|
||||||
|
# -- cert-manager Certificate resource for TLS.
|
||||||
|
tls:
|
||||||
|
enabled: false
|
||||||
|
secretName: "jellyfin-tls"
|
||||||
|
clusterIssuer: "letsencrypt-prod"
|
||||||
|
dnsNames: []
|
||||||
|
# - jellyfin.example.com
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Resource requests and limits for the Jellyfin container
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 1Gi
|
||||||
|
limits:
|
||||||
|
cpu: "4"
|
||||||
|
memory: 4Gi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Liveness and readiness probes
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 30
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Security context
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Container-level security context.
|
||||||
|
securityContext:
|
||||||
|
# -- Set to true only when GPU passthrough via /dev/dri is required.
|
||||||
|
# privileged=true is required for DRM ioctls (VA-API). Omit (false) for
|
||||||
|
# software-only transcoding.
|
||||||
|
privileged: false
|
||||||
|
# -- UID for the Jellyfin process. Use 10010 to match the svc-jellyfin NAS account
|
||||||
|
# when NFS root_squash is enabled.
|
||||||
|
runAsUser: 1000
|
||||||
|
runAsGroup: 1000
|
||||||
|
|
||||||
|
# Pod-level security context.
|
||||||
|
podSecurityContext:
|
||||||
|
# -- fsGroup ensures mounted volumes are group-writable.
|
||||||
|
fsGroup: 1000
|
||||||
|
# -- Additional groups for /dev/dri access (video=44, render=109 or 991).
|
||||||
|
supplementalGroups: []
|
||||||
|
# - 44 # video
|
||||||
|
# - 109 # render (legacy)
|
||||||
|
# - 991 # render (Debian 13 trixie)
|
||||||
|
seccompProfile:
|
||||||
|
type: RuntimeDefault
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Service account
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
serviceAccount:
|
||||||
|
create: false
|
||||||
|
name: ""
|
||||||
|
annotations: {}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Pod Disruption Budget
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
podDisruptionBudget:
|
||||||
|
enabled: true
|
||||||
|
minAvailable: 1
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Pod anti-affinity (spread replicas across nodes for node-level HA)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
podAntiAffinity:
|
||||||
|
enabled: true
|
||||||
|
# -- "preferred" won't block scheduling if nodes are insufficient.
|
||||||
|
# Use "required" to enforce strict cross-node placement.
|
||||||
|
type: preferred
|
||||||
|
weight: 100
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Prometheus ServiceMonitor
|
||||||
|
# Note: Jellyfin has no native /metrics endpoint. This ServiceMonitor is
|
||||||
|
# included for future use (e.g. if you add a sidecar exporter) or for
|
||||||
|
# blackbox-style readiness monitoring. Disable if not using kube-prometheus-stack.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
serviceMonitor:
|
||||||
|
enabled: false
|
||||||
|
# -- Scrape interval.
|
||||||
|
interval: "30s"
|
||||||
|
# -- Scrape path (Jellyfin does not expose Prometheus metrics natively).
|
||||||
|
path: /metrics
|
||||||
|
# -- Additional labels to add to the ServiceMonitor (e.g. to match a Prometheus release label).
|
||||||
|
additionalLabels: {}
|
||||||
|
# release: kube-prometheus-stack
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Runtime config (jellyfin.runtimeconfig.json)
|
||||||
|
# Set dotnet runtime switches here if needed. Leave empty for defaults.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
runtimeConfig:
|
||||||
|
enabled: false
|
||||||
|
# -- Raw JSON content for jellyfin.runtimeconfig.json.
|
||||||
|
# See jellyfin-runtimeconfig ConfigMap in the existing manifests for an example.
|
||||||
|
json: |
|
||||||
|
{
|
||||||
|
"configProperties": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Extra Kubernetes resources
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# -- Additional volumes to attach to the Jellyfin pod.
|
||||||
|
extraVolumes: []
|
||||||
|
# - name: my-extra-config
|
||||||
|
# configMap:
|
||||||
|
# name: my-configmap
|
||||||
|
|
||||||
|
# -- Additional volume mounts for the Jellyfin container.
|
||||||
|
extraVolumeMounts: []
|
||||||
|
# - name: my-extra-config
|
||||||
|
# mountPath: /etc/my-config
|
||||||
|
|
||||||
|
# -- Additional init containers.
|
||||||
|
extraInitContainers: []
|
||||||
|
|
||||||
|
# -- Annotations to add to the StatefulSet.
|
||||||
|
annotations: {}
|
||||||
|
# -- Annotations to add to individual pods.
|
||||||
|
podAnnotations: {}
|
||||||
|
# -- Labels to add to the StatefulSet.
|
||||||
|
labels: {}
|
||||||
|
# -- Labels to add to individual pods.
|
||||||
|
podLabels: {}
|
||||||
|
|
||||||
|
# -- Update strategy for the StatefulSet.
|
||||||
|
updateStrategy:
|
||||||
|
type: RollingUpdate
|
||||||
Reference in New Issue
Block a user