From 0b01f40ac84e158789c7a8917582b297b1f35911 Mon Sep 17 00:00:00 2001 From: Shahab Dogar Date: Sat, 15 Nov 2025 13:22:02 +0500 Subject: [PATCH] feat: Media | add jellyfin and related services for home media system --- media/deployment.yaml | 231 ++++++++++++++++++++++++++++++++++++++++++ media/ingress.yaml | 76 ++++++++++++++ media/namespace.yaml | 5 + media/pvc.yaml | 106 +++++++++++++++++++ media/service.yaml | 69 +++++++++++++ 5 files changed, 487 insertions(+) create mode 100644 media/deployment.yaml create mode 100644 media/ingress.yaml create mode 100644 media/namespace.yaml create mode 100644 media/pvc.yaml create mode 100644 media/service.yaml diff --git a/media/deployment.yaml b/media/deployment.yaml new file mode 100644 index 0000000..5dd97fd --- /dev/null +++ b/media/deployment.yaml @@ -0,0 +1,231 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: server + namespace: media +spec: + replicas: 1 + selector: + matchLabels: + app: server + template: + metadata: + labels: + app: server + spec: + nodeSelector: + kubernetes.io/hostname: aamil-3 + containers: + - name: server + image: jellyfin/jellyfin:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8096 + name: http + env: + - name: TZ + value: "Asia/Karachi" + volumeMounts: + - name: config + mountPath: /config + - name: cache + mountPath: /cache + - name: media + mountPath: /media + volumes: + - name: config + persistentVolumeClaim: + claimName: jellyfin-config + - name: cache + emptyDir: {} + - name: media + persistentVolumeClaim: + claimName: media +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sonarr + namespace: media +spec: + replicas: 1 + selector: + matchLabels: + app: sonarr + template: + metadata: + labels: + app: sonarr + spec: + nodeSelector: + kubernetes.io/hostname: aamil-3 + containers: + - name: sonarr + image: lscr.io/linuxserver/sonarr:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8989 + name: http + env: + - name: TZ + value: "Asia/Karachi" + - name: PUID + value: "1000" + - name: PGID + value: "1000" + volumeMounts: + - name: config + mountPath: /config + - name: media + mountPath: /media + - name: downloads + mountPath: /downloads + volumes: + - name: config + persistentVolumeClaim: + claimName: sonarr-config + - name: media + persistentVolumeClaim: + claimName: media + - name: downloads + persistentVolumeClaim: + claimName: downloads +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: qbittorrent + namespace: media +spec: + replicas: 1 + selector: + matchLabels: + app: qbittorrent + template: + metadata: + labels: + app: qbittorrent + spec: + nodeSelector: + kubernetes.io/hostname: aamil-3 + containers: + - name: qbittorrent + image: lscr.io/linuxserver/qbittorrent:latest + ports: + - containerPort: 8080 # web UI + name: http + - containerPort: 6881 + name: bt + - containerPort: 6881 + protocol: UDP + name: bt-udp + env: + - name: TZ + value: "Asia/Karachi" + - name: PUID + value: "1000" + - name: PGID + value: "1000" + - name: WEBUI_PORT + value: "8080" + volumeMounts: + - name: config + mountPath: /config + - name: downloads + mountPath: /downloads + volumes: + - name: config + persistentVolumeClaim: + claimName: qbittorrent-config + - name: downloads + persistentVolumeClaim: + claimName: downloads +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: prowlarr + namespace: media +spec: + replicas: 1 + selector: + matchLabels: + app: prowlarr + template: + metadata: + labels: + app: prowlarr + spec: + nodeSelector: + nodepool: worker + containers: + - name: prowlarr + image: lscr.io/linuxserver/prowlarr:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9696 + name: http + env: + - name: TZ + value: "Asia/Karachi" + - name: PUID + value: "1000" + - name: PGID + value: "1000" + volumeMounts: + - name: config + mountPath: /config + volumes: + - name: config + persistentVolumeClaim: + claimName: prowlarr-config +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: radarr + namespace: media +spec: + replicas: 1 + selector: + matchLabels: + app: radarr + template: + metadata: + labels: + app: radarr + spec: + nodeSelector: + kubernetes.io/hostname: aamil-3 + containers: + - name: radarr + image: lscr.io/linuxserver/radarr:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 7878 + name: http + env: + - name: TZ + value: "Asia/Karachi" + - name: PUID + value: "1000" + - name: PGID + value: "1000" + volumeMounts: + - name: config + mountPath: /config + - name: media + mountPath: /media + - name: downloads + mountPath: /downloads + volumes: + - name: config + persistentVolumeClaim: + claimName: radarr-config + - name: media + persistentVolumeClaim: + claimName: media + - name: downloads + persistentVolumeClaim: + claimName: downloads diff --git a/media/ingress.yaml b/media/ingress.yaml new file mode 100644 index 0000000..3ace2ce --- /dev/null +++ b/media/ingress.yaml @@ -0,0 +1,76 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: server + namespace: media + annotations: + # cert-manager (Cloudflare DNS-01) + cert-manager.io/cluster-issuer: cloudflare-issuer + cert-manager.io/acme-challenge-type: dns01 + cert-manager.io/private-key-size: "4096" + + # Jellyfin / streaming friendly nginx settings + nginx.ingress.kubernetes.io/proxy-body-size: "0" + nginx.ingress.kubernetes.io/proxy-buffering: "off" + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-http-version: "1.1" + nginx.ingress.kubernetes.io/use-proxy-protocol: "false" + nginx.ingress.kubernetes.io/proxy-request-buffering: "off" +spec: + ingressClassName: nginx-internal + tls: + - hosts: + - media.dogar.dev + secretName: media-tls + - hosts: + - sonarr.dogar.dev + secretName: sonarr-tls + - hosts: + - radarr.dogar.dev + secretName: radarr-tls + - hosts: + - torrent.dogar.dev + secretName: torrent-tls + rules: + - host: media.dogar.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: server + port: + number: 80 + - host: sonarr.dogar.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: sonarr + port: + number: 80 + - host: radarr.dogar.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: radarr + port: + number: 80 + - host: torrent.dogar.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: qbittorrent + port: + number: 80 diff --git a/media/namespace.yaml b/media/namespace.yaml new file mode 100644 index 0000000..6a99325 --- /dev/null +++ b/media/namespace.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: media diff --git a/media/pvc.yaml b/media/pvc.yaml new file mode 100644 index 0000000..e48fbc4 --- /dev/null +++ b/media/pvc.yaml @@ -0,0 +1,106 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: jellyfin-config + namespace: media + labels: + recurring-job.longhorn.io/source: "enabled" + recurring-job.longhorn.io/backup: "enabled" +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi + storageClassName: longhorn +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: media + namespace: media +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 512Gi + storageClassName: longhorn +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: sonarr-config + namespace: media + labels: + recurring-job.longhorn.io/source: "enabled" + recurring-job.longhorn.io/backup: "enabled" +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 512Mi + storageClassName: longhorn +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: qbittorrent-config + namespace: media + labels: + recurring-job.longhorn.io/source: "enabled" + recurring-job.longhorn.io/backup: "enabled" +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 512Mi + storageClassName: longhorn +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: downloads + namespace: media +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 450Gi + storageClassName: longhorn +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: prowlarr-config + namespace: media + labels: + recurring-job.longhorn.io/source: "enabled" + recurring-job.longhorn.io/backup: "enabled" +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 512Mi + storageClassName: longhorn +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: radarr-config + namespace: media + labels: + recurring-job.longhorn.io/source: "enabled" + recurring-job.longhorn.io/backup: "enabled" +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 512Mi + storageClassName: longhorn diff --git a/media/service.yaml b/media/service.yaml new file mode 100644 index 0000000..1bae7a8 --- /dev/null +++ b/media/service.yaml @@ -0,0 +1,69 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: server + namespace: media +spec: + selector: + app: server + ports: + - name: http + port: 80 + targetPort: 8096 + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + name: sonarr + namespace: media +spec: + selector: + app: sonarr + ports: + - name: http + port: 80 + targetPort: 8989 + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + name: qbittorrent + namespace: media +spec: + selector: + app: qbittorrent + ports: + - name: http + port: 80 + targetPort: 8080 + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + name: prowlarr + namespace: media +spec: + selector: + app: prowlarr + ports: + - name: http + port: 80 + targetPort: 9696 + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + name: radarr + namespace: media +spec: + selector: + app: radarr + ports: + - name: http + port: 80 + targetPort: 7878