diff --git a/apps/base/bazarr/deployment.yaml b/apps/base/bazarr/deployment.yaml
new file mode 100644
index 0000000..171c86b
--- /dev/null
+++ b/apps/base/bazarr/deployment.yaml
@@ -0,0 +1,61 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: bazarr
+ namespace: media-apps
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ app: bazarr
+ template:
+ metadata:
+ labels:
+ app: bazarr
+ spec:
+ securityContext:
+ fsGroup: 1000
+ containers:
+ - name: bazarr
+ image: ghcr.io/home-operations/bazarr:rolling
+ imagePullPolicy: IfNotPresent
+ ports:
+ - name: http
+ containerPort: 6767
+ protocol: TCP
+ env:
+ - name: TZ
+ value: Australia/Sydney
+ - name: PUID
+ value: "1000"
+ - name: PGID
+ value: "1000"
+ resources:
+ requests:
+ cpu: 50m
+ memory: 256Mi
+ limits:
+ cpu: "1"
+ memory: 1Gi
+ livenessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 30
+ periodSeconds: 30
+ readinessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ volumeMounts:
+ - name: config
+ mountPath: /config
+ volumes:
+ - name: config
+ persistentVolumeClaim:
+ claimName: bazarr-config
diff --git a/apps/base/bazarr/gateway.yaml b/apps/base/bazarr/gateway.yaml
new file mode 100644
index 0000000..6adf69d
--- /dev/null
+++ b/apps/base/bazarr/gateway.yaml
@@ -0,0 +1,37 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ labels:
+ traefik.io/instance: internal
+ annotations:
+ cert-manager.io/cluster-issuer: vault-issuer
+ cert-manager.io/common-name: bazarr.k8s.syd1.au.unkin.net
+ cert-manager.io/private-key-size: "4096"
+ external-dns.alpha.kubernetes.io/hostname: bazarr.k8s.syd1.au.unkin.net
+ external-dns.alpha.kubernetes.io/target: 198.18.200.4
+ name: bazarr
+ namespace: media-apps
+spec:
+ gatewayClassName: traefik-internal
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: bazarr.k8s.syd1.au.unkin.net
+ name: http
+ port: 80
+ protocol: HTTP
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: bazarr.k8s.syd1.au.unkin.net
+ name: https
+ port: 443
+ protocol: HTTPS
+ tls:
+ certificateRefs:
+ - group: ""
+ kind: Secret
+ name: bazarr-tls
+ mode: Terminate
diff --git a/apps/base/bazarr/httproute.yaml b/apps/base/bazarr/httproute.yaml
new file mode 100644
index 0000000..01de102
--- /dev/null
+++ b/apps/base/bazarr/httproute.yaml
@@ -0,0 +1,49 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: http-redirect
+ namespace: media-apps
+spec:
+ hostnames:
+ - bazarr.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: bazarr
+ sectionName: http
+ rules:
+ - filters:
+ - type: RequestRedirect
+ requestRedirect:
+ scheme: https
+ statusCode: 301
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: bazarr
+ namespace: media-apps
+spec:
+ hostnames:
+ - bazarr.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: bazarr
+ sectionName: https
+ rules:
+ - backendRefs:
+ - group: ""
+ kind: Service
+ name: bazarr
+ port: 6767
+ weight: 1
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
diff --git a/apps/base/bazarr/kustomization.yaml b/apps/base/bazarr/kustomization.yaml
new file mode 100644
index 0000000..75755b6
--- /dev/null
+++ b/apps/base/bazarr/kustomization.yaml
@@ -0,0 +1,10 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - pvc.yaml
+ - deployment.yaml
+ - service.yaml
+ - gateway.yaml
+ - httproute.yaml
diff --git a/apps/base/bazarr/pvc.yaml b/apps/base/bazarr/pvc.yaml
new file mode 100644
index 0000000..ac449c5
--- /dev/null
+++ b/apps/base/bazarr/pvc.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: bazarr-config
+ namespace: media-apps
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+ storageClassName: cephrbd-fast-delete
+ volumeMode: Filesystem
diff --git a/apps/base/bazarr/service.yaml b/apps/base/bazarr/service.yaml
new file mode 100644
index 0000000..2dfa0e4
--- /dev/null
+++ b/apps/base/bazarr/service.yaml
@@ -0,0 +1,15 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: bazarr
+ namespace: media-apps
+spec:
+ type: ClusterIP
+ selector:
+ app: bazarr
+ ports:
+ - name: http
+ port: 6767
+ targetPort: http
+ protocol: TCP
diff --git a/apps/base/jellyfin/deployment.yaml b/apps/base/jellyfin/deployment.yaml
new file mode 100644
index 0000000..0d61e6e
--- /dev/null
+++ b/apps/base/jellyfin/deployment.yaml
@@ -0,0 +1,98 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: jellyfin
+ namespace: media-apps
+spec:
+ # Single-replica for now. The jellyfin-ha fork adds the Redis transcode store
+ # and RWX transcode scratch that make scaling to true HA a follow-up.
+ replicas: 1
+ strategy:
+ # Config PVC is RWO; Recreate avoids two pods contending for it.
+ type: Recreate
+ selector:
+ matchLabels:
+ app: jellyfin
+ template:
+ metadata:
+ labels:
+ app: jellyfin
+ spec:
+ securityContext:
+ fsGroup: 1000
+ nodeSelector:
+ feature.node.kubernetes.io/pci-0300_8086.present: "true"
+ containers:
+ - name: jellyfin
+ image: git.unkin.net/unkin/jellyfin-ha:v0.1.0
+ imagePullPolicy: IfNotPresent
+ ports:
+ - name: http
+ containerPort: 8096
+ protocol: TCP
+ env:
+ - name: TZ
+ value: Australia/Sydney
+ - name: PUID
+ value: "1000"
+ - name: PGID
+ value: "1000"
+ - name: JELLYFIN_PublishedServerUrl
+ value: https://jellyfin.k8s.syd1.au.unkin.net
+ # Distributed transcode session store (jellyfin-ha additions).
+ - name: Jellyfin__TranscodeStore__RedisConnectionString
+ value: "jellyfin-redis:6379,abortConnect=false"
+ - name: Jellyfin__TranscodeStore__LeaseDurationSeconds
+ value: "30"
+ resources:
+ requests:
+ cpu: 100m
+ memory: 1Gi
+ limits:
+ cpu: "4"
+ memory: 8Gi
+ gpu.intel.com/i915: "1"
+ 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
+ volumeMounts:
+ - name: config
+ mountPath: /config
+ - name: cache
+ mountPath: /cache
+ - name: transcode
+ mountPath: /transcode
+ - name: movies
+ mountPath: /mnt/movies
+ - name: tvseries
+ mountPath: /mnt/tvseries
+ volumes:
+ - name: config
+ persistentVolumeClaim:
+ claimName: jellyfin-config
+ - name: cache
+ persistentVolumeClaim:
+ claimName: jellyfin-cache
+ - name: transcode
+ persistentVolumeClaim:
+ claimName: jellyfin-transcode
+ - name: movies
+ persistentVolumeClaim:
+ claimName: movies
+ - name: tvseries
+ persistentVolumeClaim:
+ claimName: tvseries
diff --git a/apps/base/jellyfin/gateway.yaml b/apps/base/jellyfin/gateway.yaml
new file mode 100644
index 0000000..1ed5c15
--- /dev/null
+++ b/apps/base/jellyfin/gateway.yaml
@@ -0,0 +1,37 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ labels:
+ traefik.io/instance: internal
+ annotations:
+ cert-manager.io/cluster-issuer: vault-issuer
+ cert-manager.io/common-name: jellyfin.k8s.syd1.au.unkin.net
+ 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: media-apps
+spec:
+ gatewayClassName: traefik-internal
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: jellyfin.k8s.syd1.au.unkin.net
+ name: http
+ port: 80
+ protocol: HTTP
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: jellyfin.k8s.syd1.au.unkin.net
+ name: https
+ port: 443
+ protocol: HTTPS
+ tls:
+ certificateRefs:
+ - group: ""
+ kind: Secret
+ name: jellyfin-tls
+ mode: Terminate
diff --git a/apps/base/jellyfin/httproute.yaml b/apps/base/jellyfin/httproute.yaml
new file mode 100644
index 0000000..b6e98a8
--- /dev/null
+++ b/apps/base/jellyfin/httproute.yaml
@@ -0,0 +1,49 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: http-redirect
+ namespace: media-apps
+spec:
+ hostnames:
+ - jellyfin.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: jellyfin
+ sectionName: http
+ rules:
+ - filters:
+ - type: RequestRedirect
+ requestRedirect:
+ scheme: https
+ statusCode: 301
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: jellyfin
+ namespace: media-apps
+spec:
+ hostnames:
+ - jellyfin.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: jellyfin
+ sectionName: https
+ rules:
+ - backendRefs:
+ - group: ""
+ kind: Service
+ name: jellyfin
+ port: 8096
+ weight: 1
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
diff --git a/apps/base/jellyfin/kustomization.yaml b/apps/base/jellyfin/kustomization.yaml
new file mode 100644
index 0000000..219982c
--- /dev/null
+++ b/apps/base/jellyfin/kustomization.yaml
@@ -0,0 +1,15 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - pvc_config.yaml
+ - pvc_cache.yaml
+ - pvc_transcode.yaml
+ - deployment.yaml
+ - service.yaml
+ - redis-deployment.yaml
+ - redis-service.yaml
+ - redis-pvc.yaml
+ - gateway.yaml
+ - httproute.yaml
diff --git a/apps/base/jellyfin/pvc_cache.yaml b/apps/base/jellyfin/pvc_cache.yaml
new file mode 100644
index 0000000..c412c5d
--- /dev/null
+++ b/apps/base/jellyfin/pvc_cache.yaml
@@ -0,0 +1,15 @@
+---
+# Local transcode/image cache. Scratch, delete reclaim policy.
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: jellyfin-cache
+ namespace: media-apps
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 200Gi
+ storageClassName: cephrbd-fast-delete
+ volumeMode: Filesystem
diff --git a/apps/base/jellyfin/pvc_config.yaml b/apps/base/jellyfin/pvc_config.yaml
new file mode 100644
index 0000000..0e23c33
--- /dev/null
+++ b/apps/base/jellyfin/pvc_config.yaml
@@ -0,0 +1,15 @@
+---
+# Jellyfin config + SQLite library database. Single-writer, block storage.
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: jellyfin-config
+ namespace: media-apps
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 10Gi
+ storageClassName: cephrbd-fast-retain
+ volumeMode: Filesystem
diff --git a/apps/base/jellyfin/pvc_transcode.yaml b/apps/base/jellyfin/pvc_transcode.yaml
new file mode 100644
index 0000000..7c25b61
--- /dev/null
+++ b/apps/base/jellyfin/pvc_transcode.yaml
@@ -0,0 +1,17 @@
+---
+# Shared transcode scratch. ReadWriteMany is the hard requirement for the HA
+# fork: a taking-over pod must read the in-flight HLS segments written by the
+# pod it replaces. Scratch data, so delete reclaim policy.
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: jellyfin-transcode
+ namespace: media-apps
+spec:
+ accessModes:
+ - ReadWriteMany
+ resources:
+ requests:
+ storage: 100Gi
+ storageClassName: cephfs-raid5-delete
+ volumeMode: Filesystem
diff --git a/apps/base/jellyfin/redis-deployment.yaml b/apps/base/jellyfin/redis-deployment.yaml
new file mode 100644
index 0000000..141a95b
--- /dev/null
+++ b/apps/base/jellyfin/redis-deployment.yaml
@@ -0,0 +1,64 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: jellyfin-redis
+ namespace: media-apps
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ app: jellyfin-redis
+ template:
+ metadata:
+ labels:
+ app: jellyfin-redis
+ spec:
+ restartPolicy: Always
+ containers:
+ - name: redis
+ image: redis:7-alpine
+ imagePullPolicy: IfNotPresent
+ command:
+ - redis-server
+ - --save
+ - "20"
+ - "1"
+ ports:
+ - name: redis
+ containerPort: 6379
+ protocol: TCP
+ livenessProbe:
+ exec:
+ command:
+ - redis-cli
+ - ping
+ initialDelaySeconds: 30
+ periodSeconds: 30
+ timeoutSeconds: 5
+ failureThreshold: 3
+ readinessProbe:
+ exec:
+ command:
+ - redis-cli
+ - ping
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ timeoutSeconds: 5
+ failureThreshold: 3
+ resources:
+ requests:
+ cpu: 50m
+ memory: 128Mi
+ limits:
+ cpu: 500m
+ memory: 512Mi
+ volumeMounts:
+ - name: data
+ mountPath: /data
+ volumes:
+ - name: data
+ persistentVolumeClaim:
+ claimName: jellyfin-redis-data
diff --git a/apps/base/jellyfin/redis-pvc.yaml b/apps/base/jellyfin/redis-pvc.yaml
new file mode 100644
index 0000000..0916f96
--- /dev/null
+++ b/apps/base/jellyfin/redis-pvc.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: jellyfin-redis-data
+ namespace: media-apps
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 5Gi
+ storageClassName: cephrbd-fast-delete
+ volumeMode: Filesystem
diff --git a/apps/base/jellyfin/redis-service.yaml b/apps/base/jellyfin/redis-service.yaml
new file mode 100644
index 0000000..d1e2181
--- /dev/null
+++ b/apps/base/jellyfin/redis-service.yaml
@@ -0,0 +1,17 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: jellyfin-redis
+ namespace: media-apps
+spec:
+ type: ClusterIP
+ internalTrafficPolicy: Cluster
+ sessionAffinity: None
+ selector:
+ app: jellyfin-redis
+ ports:
+ - name: redis
+ port: 6379
+ targetPort: redis
+ protocol: TCP
diff --git a/apps/base/jellyfin/service.yaml b/apps/base/jellyfin/service.yaml
new file mode 100644
index 0000000..2722963
--- /dev/null
+++ b/apps/base/jellyfin/service.yaml
@@ -0,0 +1,17 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: jellyfin
+ namespace: media-apps
+spec:
+ type: ClusterIP
+ internalTrafficPolicy: Cluster
+ sessionAffinity: None
+ selector:
+ app: jellyfin
+ ports:
+ - name: http
+ port: 8096
+ targetPort: http
+ protocol: TCP
diff --git a/apps/base/jellyseerr/deployment.yaml b/apps/base/jellyseerr/deployment.yaml
new file mode 100644
index 0000000..3ff94ad
--- /dev/null
+++ b/apps/base/jellyseerr/deployment.yaml
@@ -0,0 +1,61 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: jellyseerr
+ namespace: media-apps
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ app: jellyseerr
+ template:
+ metadata:
+ labels:
+ app: jellyseerr
+ spec:
+ securityContext:
+ fsGroup: 1000
+ containers:
+ - name: jellyseerr
+ image: ghcr.io/fallenbagel/jellyseerr:latest
+ imagePullPolicy: IfNotPresent
+ ports:
+ - name: http
+ containerPort: 5055
+ protocol: TCP
+ env:
+ - name: TZ
+ value: Australia/Sydney
+ - name: LOG_LEVEL
+ value: debug
+ - name: PORT
+ value: "5055"
+ resources:
+ requests:
+ cpu: 50m
+ memory: 128Mi
+ limits:
+ cpu: "1"
+ memory: 1Gi
+ livenessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 30
+ periodSeconds: 30
+ readinessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ volumeMounts:
+ - name: config
+ mountPath: /app/config
+ volumes:
+ - name: config
+ persistentVolumeClaim:
+ claimName: jellyseerr-config
diff --git a/apps/base/jellyseerr/gateway.yaml b/apps/base/jellyseerr/gateway.yaml
new file mode 100644
index 0000000..01ea213
--- /dev/null
+++ b/apps/base/jellyseerr/gateway.yaml
@@ -0,0 +1,37 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ labels:
+ traefik.io/instance: internal
+ annotations:
+ cert-manager.io/cluster-issuer: vault-issuer
+ cert-manager.io/common-name: jellyseerr.k8s.syd1.au.unkin.net
+ cert-manager.io/private-key-size: "4096"
+ external-dns.alpha.kubernetes.io/hostname: jellyseerr.k8s.syd1.au.unkin.net
+ external-dns.alpha.kubernetes.io/target: 198.18.200.4
+ name: jellyseerr
+ namespace: media-apps
+spec:
+ gatewayClassName: traefik-internal
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: jellyseerr.k8s.syd1.au.unkin.net
+ name: http
+ port: 80
+ protocol: HTTP
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: jellyseerr.k8s.syd1.au.unkin.net
+ name: https
+ port: 443
+ protocol: HTTPS
+ tls:
+ certificateRefs:
+ - group: ""
+ kind: Secret
+ name: jellyseerr-tls
+ mode: Terminate
diff --git a/apps/base/jellyseerr/httproute.yaml b/apps/base/jellyseerr/httproute.yaml
new file mode 100644
index 0000000..7f3d770
--- /dev/null
+++ b/apps/base/jellyseerr/httproute.yaml
@@ -0,0 +1,49 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: http-redirect
+ namespace: media-apps
+spec:
+ hostnames:
+ - jellyseerr.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: jellyseerr
+ sectionName: http
+ rules:
+ - filters:
+ - type: RequestRedirect
+ requestRedirect:
+ scheme: https
+ statusCode: 301
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: jellyseerr
+ namespace: media-apps
+spec:
+ hostnames:
+ - jellyseerr.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: jellyseerr
+ sectionName: https
+ rules:
+ - backendRefs:
+ - group: ""
+ kind: Service
+ name: jellyseerr
+ port: 5055
+ weight: 1
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
diff --git a/apps/base/jellyseerr/kustomization.yaml b/apps/base/jellyseerr/kustomization.yaml
new file mode 100644
index 0000000..75755b6
--- /dev/null
+++ b/apps/base/jellyseerr/kustomization.yaml
@@ -0,0 +1,10 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - pvc.yaml
+ - deployment.yaml
+ - service.yaml
+ - gateway.yaml
+ - httproute.yaml
diff --git a/apps/base/jellyseerr/pvc.yaml b/apps/base/jellyseerr/pvc.yaml
new file mode 100644
index 0000000..8ad62e5
--- /dev/null
+++ b/apps/base/jellyseerr/pvc.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: jellyseerr-config
+ namespace: media-apps
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+ storageClassName: cephrbd-fast-delete
+ volumeMode: Filesystem
diff --git a/apps/base/jellyseerr/service.yaml b/apps/base/jellyseerr/service.yaml
new file mode 100644
index 0000000..5df32ae
--- /dev/null
+++ b/apps/base/jellyseerr/service.yaml
@@ -0,0 +1,15 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: jellyseerr
+ namespace: media-apps
+spec:
+ type: ClusterIP
+ selector:
+ app: jellyseerr
+ ports:
+ - name: http
+ port: 5055
+ targetPort: http
+ protocol: TCP
diff --git a/apps/base/media-apps/kustomization.yaml b/apps/base/media-apps/kustomization.yaml
new file mode 100644
index 0000000..73ab92a
--- /dev/null
+++ b/apps/base/media-apps/kustomization.yaml
@@ -0,0 +1,10 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - namespace.yaml
+ - serviceaccount.yaml
+ - vaultauth.yaml
+ - pvc_movies.yaml
+ - pvc_tvseries.yaml
diff --git a/apps/base/media-apps/namespace.yaml b/apps/base/media-apps/namespace.yaml
new file mode 100644
index 0000000..f55cc8e
--- /dev/null
+++ b/apps/base/media-apps/namespace.yaml
@@ -0,0 +1,5 @@
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: media-apps
diff --git a/apps/base/media-apps/pvc_movies.yaml b/apps/base/media-apps/pvc_movies.yaml
new file mode 100644
index 0000000..e90dabe
--- /dev/null
+++ b/apps/base/media-apps/pvc_movies.yaml
@@ -0,0 +1,16 @@
+---
+# Shared movies library, read-write-many across the *arr stack and jellyfin.
+# Greenfield-empty: a fresh CephFS volume, not the puppet mediafs library.
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: movies
+ namespace: media-apps
+spec:
+ accessModes:
+ - ReadWriteMany
+ resources:
+ requests:
+ storage: 500Gi
+ storageClassName: cephfs-raid5-retain
+ volumeMode: Filesystem
diff --git a/apps/base/media-apps/pvc_tvseries.yaml b/apps/base/media-apps/pvc_tvseries.yaml
new file mode 100644
index 0000000..7218af6
--- /dev/null
+++ b/apps/base/media-apps/pvc_tvseries.yaml
@@ -0,0 +1,16 @@
+---
+# Shared tvseries library, read-write-many across the *arr stack and jellyfin.
+# Greenfield-empty: a fresh CephFS volume, not the puppet mediafs library.
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: tvseries
+ namespace: media-apps
+spec:
+ accessModes:
+ - ReadWriteMany
+ resources:
+ requests:
+ storage: 500Gi
+ storageClassName: cephfs-raid5-retain
+ volumeMode: Filesystem
diff --git a/apps/base/media-apps/serviceaccount.yaml b/apps/base/media-apps/serviceaccount.yaml
new file mode 100644
index 0000000..5713b6d
--- /dev/null
+++ b/apps/base/media-apps/serviceaccount.yaml
@@ -0,0 +1,12 @@
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: media-apps-vault-reader
+ namespace: media-apps
+ labels:
+ app.kubernetes.io/name: vault-reader
+ app.kubernetes.io/component: service-account
+ annotations:
+ vault.hashicorp.com/auth-method: kubernetes
+automountServiceAccountToken: true
diff --git a/apps/base/media-apps/vaultauth.yaml b/apps/base/media-apps/vaultauth.yaml
new file mode 100644
index 0000000..492e2f2
--- /dev/null
+++ b/apps/base/media-apps/vaultauth.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: secrets.hashicorp.com/v1beta1
+kind: VaultAuth
+metadata:
+ name: default
+ namespace: media-apps
+spec:
+ allowedNamespaces:
+ - media-apps
+ kubernetes:
+ audiences:
+ - vault
+ role: media-apps
+ serviceAccount: media-apps-vault-reader
+ tokenExpirationSeconds: 600
+ method: kubernetes
+ mount: k8s/au/syd1
+ vaultConnectionRef: vso-system/default
diff --git a/apps/base/nzbget/deployment.yaml b/apps/base/nzbget/deployment.yaml
new file mode 100644
index 0000000..e15e41c
--- /dev/null
+++ b/apps/base/nzbget/deployment.yaml
@@ -0,0 +1,81 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: nzbget
+ namespace: media-apps
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ app: nzbget
+ template:
+ metadata:
+ labels:
+ app: nzbget
+ spec:
+ securityContext:
+ fsGroup: 1000
+ containers:
+ - name: nzbget
+ image: ghcr.io/home-operations/nzbget:rolling
+ imagePullPolicy: IfNotPresent
+ ports:
+ - name: http
+ containerPort: 6789
+ protocol: TCP
+ env:
+ - name: TZ
+ value: Australia/Sydney
+ - name: PUID
+ value: "1000"
+ - name: PGID
+ value: "1000"
+ - name: NZBGET_USER
+ valueFrom:
+ secretKeyRef:
+ name: nzbget-secrets
+ key: NZBGET_USER
+ - name: NZBGET_PASS
+ valueFrom:
+ secretKeyRef:
+ name: nzbget-secrets
+ key: NZBGET_PASS
+ resources:
+ requests:
+ cpu: 100m
+ memory: 512Mi
+ limits:
+ cpu: "4"
+ memory: 8Gi
+ livenessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 30
+ periodSeconds: 30
+ readinessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ volumeMounts:
+ - name: config
+ mountPath: /config
+ - name: movies
+ mountPath: /mnt/movies
+ - name: tvseries
+ mountPath: /mnt/tvseries
+ volumes:
+ - name: config
+ persistentVolumeClaim:
+ claimName: nzbget-config
+ - name: movies
+ persistentVolumeClaim:
+ claimName: movies
+ - name: tvseries
+ persistentVolumeClaim:
+ claimName: tvseries
diff --git a/apps/base/nzbget/gateway.yaml b/apps/base/nzbget/gateway.yaml
new file mode 100644
index 0000000..0ce61ec
--- /dev/null
+++ b/apps/base/nzbget/gateway.yaml
@@ -0,0 +1,37 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ labels:
+ traefik.io/instance: internal
+ annotations:
+ cert-manager.io/cluster-issuer: vault-issuer
+ cert-manager.io/common-name: nzbget.k8s.syd1.au.unkin.net
+ cert-manager.io/private-key-size: "4096"
+ external-dns.alpha.kubernetes.io/hostname: nzbget.k8s.syd1.au.unkin.net
+ external-dns.alpha.kubernetes.io/target: 198.18.200.4
+ name: nzbget
+ namespace: media-apps
+spec:
+ gatewayClassName: traefik-internal
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: nzbget.k8s.syd1.au.unkin.net
+ name: http
+ port: 80
+ protocol: HTTP
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: nzbget.k8s.syd1.au.unkin.net
+ name: https
+ port: 443
+ protocol: HTTPS
+ tls:
+ certificateRefs:
+ - group: ""
+ kind: Secret
+ name: nzbget-tls
+ mode: Terminate
diff --git a/apps/base/nzbget/httproute.yaml b/apps/base/nzbget/httproute.yaml
new file mode 100644
index 0000000..492d89d
--- /dev/null
+++ b/apps/base/nzbget/httproute.yaml
@@ -0,0 +1,49 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: http-redirect
+ namespace: media-apps
+spec:
+ hostnames:
+ - nzbget.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: nzbget
+ sectionName: http
+ rules:
+ - filters:
+ - type: RequestRedirect
+ requestRedirect:
+ scheme: https
+ statusCode: 301
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: nzbget
+ namespace: media-apps
+spec:
+ hostnames:
+ - nzbget.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: nzbget
+ sectionName: https
+ rules:
+ - backendRefs:
+ - group: ""
+ kind: Service
+ name: nzbget
+ port: 6789
+ weight: 1
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
diff --git a/apps/base/nzbget/kustomization.yaml b/apps/base/nzbget/kustomization.yaml
new file mode 100644
index 0000000..2e2d281
--- /dev/null
+++ b/apps/base/nzbget/kustomization.yaml
@@ -0,0 +1,11 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - pvc.yaml
+ - vaultstaticsecret.yaml
+ - deployment.yaml
+ - service.yaml
+ - gateway.yaml
+ - httproute.yaml
diff --git a/apps/base/nzbget/pvc.yaml b/apps/base/nzbget/pvc.yaml
new file mode 100644
index 0000000..5b48cc6
--- /dev/null
+++ b/apps/base/nzbget/pvc.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: nzbget-config
+ namespace: media-apps
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+ storageClassName: cephrbd-fast-delete
+ volumeMode: Filesystem
diff --git a/apps/base/nzbget/service.yaml b/apps/base/nzbget/service.yaml
new file mode 100644
index 0000000..405db96
--- /dev/null
+++ b/apps/base/nzbget/service.yaml
@@ -0,0 +1,15 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: nzbget
+ namespace: media-apps
+spec:
+ type: ClusterIP
+ selector:
+ app: nzbget
+ ports:
+ - name: http
+ port: 6789
+ targetPort: http
+ protocol: TCP
diff --git a/apps/base/nzbget/vaultstaticsecret.yaml b/apps/base/nzbget/vaultstaticsecret.yaml
new file mode 100644
index 0000000..495fd09
--- /dev/null
+++ b/apps/base/nzbget/vaultstaticsecret.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: secrets.hashicorp.com/v1beta1
+kind: VaultStaticSecret
+metadata:
+ name: nzbget
+ namespace: media-apps
+spec:
+ destination:
+ create: true
+ name: nzbget-secrets
+ mount: kv
+ path: service/media-apps/nzbget
+ refreshAfter: 30s
+ type: kv-v2
+ vaultAuthRef: default
+ rolloutRestartTargets:
+ - kind: Deployment
+ name: nzbget
diff --git a/apps/base/prowlarr/configmap.yaml b/apps/base/prowlarr/configmap.yaml
new file mode 100644
index 0000000..49aa186
--- /dev/null
+++ b/apps/base/prowlarr/configmap.yaml
@@ -0,0 +1,25 @@
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: prowlarr-config-xml
+ namespace: media-apps
+data:
+ config.xml: |
+
+ *
+ 9696
+ 9898
+ False
+ True
+ {{API_KEY}}
+ External
+ Enabled
+ info
+
+
+
+ prowlarr
+ Docker
+ False
+
diff --git a/apps/base/prowlarr/deployment.yaml b/apps/base/prowlarr/deployment.yaml
new file mode 100644
index 0000000..6f30cab
--- /dev/null
+++ b/apps/base/prowlarr/deployment.yaml
@@ -0,0 +1,136 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: prowlarr
+ namespace: media-apps
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ app: prowlarr
+ template:
+ metadata:
+ labels:
+ app: prowlarr
+ spec:
+ securityContext:
+ fsGroup: 1000
+ initContainers:
+ - name: config-template
+ image: busybox:latest
+ imagePullPolicy: IfNotPresent
+ command: ["/bin/sh"]
+ args:
+ - -c
+ - |
+ cp /config-template/config.xml /config/config.xml
+ sed -i "s/{{API_KEY}}/$API_KEY/g" /config/config.xml
+ env:
+ - name: API_KEY
+ valueFrom:
+ secretKeyRef:
+ name: prowlarr-secrets
+ key: apitoken
+ securityContext:
+ runAsUser: 1000
+ runAsGroup: 1000
+ runAsNonRoot: true
+ volumeMounts:
+ - name: config-template
+ mountPath: /config-template
+ - name: config
+ mountPath: /config
+ containers:
+ - name: prowlarr
+ image: ghcr.io/home-operations/prowlarr:rolling
+ imagePullPolicy: IfNotPresent
+ ports:
+ - name: http
+ containerPort: 9696
+ protocol: TCP
+ env:
+ - name: TZ
+ value: Australia/Sydney
+ - name: PUID
+ value: "1000"
+ - name: PGID
+ value: "1000"
+ resources:
+ requests:
+ cpu: 50m
+ memory: 128Mi
+ limits:
+ cpu: "1"
+ memory: 1Gi
+ livenessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 30
+ periodSeconds: 30
+ readinessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ volumeMounts:
+ - name: config
+ mountPath: /config
+ - name: exportarr
+ image: ghcr.io/onedr0p/exportarr:latest
+ imagePullPolicy: IfNotPresent
+ args: ["prowlarr"]
+ ports:
+ - name: metrics
+ containerPort: 9707
+ protocol: TCP
+ env:
+ - name: TZ
+ value: Australia/Sydney
+ - name: PORT
+ value: "9707"
+ - name: URL
+ value: "http://localhost:9696"
+ - name: API_KEY
+ valueFrom:
+ secretKeyRef:
+ name: prowlarr-secrets
+ key: apitoken
+ resources:
+ requests:
+ cpu: 50m
+ memory: 128Mi
+ limits:
+ cpu: 250m
+ memory: 256Mi
+ securityContext:
+ runAsUser: 1000
+ runAsGroup: 1000
+ runAsNonRoot: true
+ livenessProbe:
+ httpGet:
+ path: /metrics
+ port: metrics
+ initialDelaySeconds: 30
+ periodSeconds: 30
+ readinessProbe:
+ httpGet:
+ path: /metrics
+ port: metrics
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ volumeMounts:
+ - name: config
+ mountPath: /config
+ readOnly: true
+ volumes:
+ - name: config
+ persistentVolumeClaim:
+ claimName: prowlarr-config
+ - name: config-template
+ configMap:
+ name: prowlarr-config-xml
diff --git a/apps/base/prowlarr/gateway.yaml b/apps/base/prowlarr/gateway.yaml
new file mode 100644
index 0000000..fcd558c
--- /dev/null
+++ b/apps/base/prowlarr/gateway.yaml
@@ -0,0 +1,37 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ labels:
+ traefik.io/instance: internal
+ annotations:
+ cert-manager.io/cluster-issuer: vault-issuer
+ cert-manager.io/common-name: prowlarr.k8s.syd1.au.unkin.net
+ cert-manager.io/private-key-size: "4096"
+ external-dns.alpha.kubernetes.io/hostname: prowlarr.k8s.syd1.au.unkin.net
+ external-dns.alpha.kubernetes.io/target: 198.18.200.4
+ name: prowlarr
+ namespace: media-apps
+spec:
+ gatewayClassName: traefik-internal
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: prowlarr.k8s.syd1.au.unkin.net
+ name: http
+ port: 80
+ protocol: HTTP
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: prowlarr.k8s.syd1.au.unkin.net
+ name: https
+ port: 443
+ protocol: HTTPS
+ tls:
+ certificateRefs:
+ - group: ""
+ kind: Secret
+ name: prowlarr-tls
+ mode: Terminate
diff --git a/apps/base/prowlarr/httproute.yaml b/apps/base/prowlarr/httproute.yaml
new file mode 100644
index 0000000..b93cf1c
--- /dev/null
+++ b/apps/base/prowlarr/httproute.yaml
@@ -0,0 +1,49 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: http-redirect
+ namespace: media-apps
+spec:
+ hostnames:
+ - prowlarr.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: prowlarr
+ sectionName: http
+ rules:
+ - filters:
+ - type: RequestRedirect
+ requestRedirect:
+ scheme: https
+ statusCode: 301
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: prowlarr
+ namespace: media-apps
+spec:
+ hostnames:
+ - prowlarr.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: prowlarr
+ sectionName: https
+ rules:
+ - backendRefs:
+ - group: ""
+ kind: Service
+ name: prowlarr
+ port: 9696
+ weight: 1
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
diff --git a/apps/base/prowlarr/kustomization.yaml b/apps/base/prowlarr/kustomization.yaml
new file mode 100644
index 0000000..7552699
--- /dev/null
+++ b/apps/base/prowlarr/kustomization.yaml
@@ -0,0 +1,12 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - pvc.yaml
+ - configmap.yaml
+ - vaultstaticsecret.yaml
+ - deployment.yaml
+ - service.yaml
+ - gateway.yaml
+ - httproute.yaml
diff --git a/apps/base/prowlarr/pvc.yaml b/apps/base/prowlarr/pvc.yaml
new file mode 100644
index 0000000..7e9f7c8
--- /dev/null
+++ b/apps/base/prowlarr/pvc.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: prowlarr-config
+ namespace: media-apps
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+ storageClassName: cephrbd-fast-delete
+ volumeMode: Filesystem
diff --git a/apps/base/prowlarr/service.yaml b/apps/base/prowlarr/service.yaml
new file mode 100644
index 0000000..6e41bd7
--- /dev/null
+++ b/apps/base/prowlarr/service.yaml
@@ -0,0 +1,30 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: prowlarr
+ namespace: media-apps
+spec:
+ type: ClusterIP
+ selector:
+ app: prowlarr
+ ports:
+ - name: http
+ port: 9696
+ targetPort: http
+ protocol: TCP
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: prowlarr-metrics
+ namespace: media-apps
+spec:
+ type: ClusterIP
+ selector:
+ app: prowlarr
+ ports:
+ - name: metrics
+ port: 9707
+ targetPort: metrics
+ protocol: TCP
diff --git a/apps/base/prowlarr/vaultstaticsecret.yaml b/apps/base/prowlarr/vaultstaticsecret.yaml
new file mode 100644
index 0000000..e029fec
--- /dev/null
+++ b/apps/base/prowlarr/vaultstaticsecret.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: secrets.hashicorp.com/v1beta1
+kind: VaultStaticSecret
+metadata:
+ name: prowlarr
+ namespace: media-apps
+spec:
+ destination:
+ create: true
+ name: prowlarr-secrets
+ mount: kv
+ path: service/media-apps/prowlarr
+ refreshAfter: 30s
+ type: kv-v2
+ vaultAuthRef: default
+ rolloutRestartTargets:
+ - kind: Deployment
+ name: prowlarr
diff --git a/apps/base/radarr/configmap.yaml b/apps/base/radarr/configmap.yaml
new file mode 100644
index 0000000..143e36f
--- /dev/null
+++ b/apps/base/radarr/configmap.yaml
@@ -0,0 +1,25 @@
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: radarr-config-xml
+ namespace: media-apps
+data:
+ config.xml: |
+
+ *
+ 7878
+ 9898
+ False
+ True
+ {{API_KEY}}
+ External
+ Enabled
+ debug
+
+
+
+ Radarr
+ Docker
+ False
+
diff --git a/apps/base/radarr/deployment.yaml b/apps/base/radarr/deployment.yaml
new file mode 100644
index 0000000..a9489fb
--- /dev/null
+++ b/apps/base/radarr/deployment.yaml
@@ -0,0 +1,139 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: radarr
+ namespace: media-apps
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ app: radarr
+ template:
+ metadata:
+ labels:
+ app: radarr
+ spec:
+ securityContext:
+ fsGroup: 1000
+ initContainers:
+ - name: config-template
+ image: busybox:latest
+ imagePullPolicy: IfNotPresent
+ command: ["/bin/sh"]
+ args:
+ - -c
+ - |
+ cp /config-template/config.xml /config/config.xml
+ sed -i "s/{{API_KEY}}/$API_KEY/g" /config/config.xml
+ env:
+ - name: API_KEY
+ valueFrom:
+ secretKeyRef:
+ name: radarr-secrets
+ key: apitoken
+ securityContext:
+ runAsUser: 1000
+ runAsGroup: 1000
+ runAsNonRoot: true
+ volumeMounts:
+ - name: config-template
+ mountPath: /config-template
+ - name: config
+ mountPath: /config
+ containers:
+ - name: radarr
+ image: ghcr.io/home-operations/radarr:rolling
+ imagePullPolicy: IfNotPresent
+ ports:
+ - name: http
+ containerPort: 7878
+ protocol: TCP
+ env:
+ - name: TZ
+ value: Australia/Sydney
+ - name: PORT
+ value: "7878"
+ resources:
+ requests:
+ cpu: 50m
+ memory: 128Mi
+ limits:
+ cpu: "1"
+ memory: 1Gi
+ livenessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 30
+ periodSeconds: 30
+ readinessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ volumeMounts:
+ - name: config
+ mountPath: /config
+ - name: movies
+ mountPath: /mnt/movies
+ - name: exportarr
+ image: ghcr.io/onedr0p/exportarr:latest
+ imagePullPolicy: IfNotPresent
+ args: ["radarr"]
+ ports:
+ - name: metrics
+ containerPort: 9707
+ protocol: TCP
+ env:
+ - name: TZ
+ value: Australia/Sydney
+ - name: PORT
+ value: "9707"
+ - name: URL
+ value: "http://localhost:7878"
+ - name: API_KEY
+ valueFrom:
+ secretKeyRef:
+ name: radarr-secrets
+ key: apitoken
+ resources:
+ requests:
+ cpu: 50m
+ memory: 128Mi
+ limits:
+ cpu: 250m
+ memory: 256Mi
+ securityContext:
+ runAsUser: 1000
+ runAsGroup: 1000
+ runAsNonRoot: true
+ livenessProbe:
+ httpGet:
+ path: /metrics
+ port: metrics
+ initialDelaySeconds: 30
+ periodSeconds: 30
+ readinessProbe:
+ httpGet:
+ path: /metrics
+ port: metrics
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ volumeMounts:
+ - name: config
+ mountPath: /config
+ readOnly: true
+ volumes:
+ - name: config
+ persistentVolumeClaim:
+ claimName: radarr-config
+ - name: config-template
+ configMap:
+ name: radarr-config-xml
+ - name: movies
+ persistentVolumeClaim:
+ claimName: movies
diff --git a/apps/base/radarr/gateway.yaml b/apps/base/radarr/gateway.yaml
new file mode 100644
index 0000000..78b76a0
--- /dev/null
+++ b/apps/base/radarr/gateway.yaml
@@ -0,0 +1,37 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ labels:
+ traefik.io/instance: internal
+ annotations:
+ cert-manager.io/cluster-issuer: vault-issuer
+ cert-manager.io/common-name: radarr.k8s.syd1.au.unkin.net
+ cert-manager.io/private-key-size: "4096"
+ external-dns.alpha.kubernetes.io/hostname: radarr.k8s.syd1.au.unkin.net
+ external-dns.alpha.kubernetes.io/target: 198.18.200.4
+ name: radarr
+ namespace: media-apps
+spec:
+ gatewayClassName: traefik-internal
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: radarr.k8s.syd1.au.unkin.net
+ name: http
+ port: 80
+ protocol: HTTP
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: radarr.k8s.syd1.au.unkin.net
+ name: https
+ port: 443
+ protocol: HTTPS
+ tls:
+ certificateRefs:
+ - group: ""
+ kind: Secret
+ name: radarr-tls
+ mode: Terminate
diff --git a/apps/base/radarr/httproute.yaml b/apps/base/radarr/httproute.yaml
new file mode 100644
index 0000000..684cfea
--- /dev/null
+++ b/apps/base/radarr/httproute.yaml
@@ -0,0 +1,49 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: http-redirect
+ namespace: media-apps
+spec:
+ hostnames:
+ - radarr.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: radarr
+ sectionName: http
+ rules:
+ - filters:
+ - type: RequestRedirect
+ requestRedirect:
+ scheme: https
+ statusCode: 301
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: radarr
+ namespace: media-apps
+spec:
+ hostnames:
+ - radarr.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: radarr
+ sectionName: https
+ rules:
+ - backendRefs:
+ - group: ""
+ kind: Service
+ name: radarr
+ port: 7878
+ weight: 1
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
diff --git a/apps/base/radarr/kustomization.yaml b/apps/base/radarr/kustomization.yaml
new file mode 100644
index 0000000..7552699
--- /dev/null
+++ b/apps/base/radarr/kustomization.yaml
@@ -0,0 +1,12 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - pvc.yaml
+ - configmap.yaml
+ - vaultstaticsecret.yaml
+ - deployment.yaml
+ - service.yaml
+ - gateway.yaml
+ - httproute.yaml
diff --git a/apps/base/radarr/pvc.yaml b/apps/base/radarr/pvc.yaml
new file mode 100644
index 0000000..557d93b
--- /dev/null
+++ b/apps/base/radarr/pvc.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: radarr-config
+ namespace: media-apps
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+ storageClassName: cephrbd-fast-delete
+ volumeMode: Filesystem
diff --git a/apps/base/radarr/service.yaml b/apps/base/radarr/service.yaml
new file mode 100644
index 0000000..86bc821
--- /dev/null
+++ b/apps/base/radarr/service.yaml
@@ -0,0 +1,30 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: radarr
+ namespace: media-apps
+spec:
+ type: ClusterIP
+ selector:
+ app: radarr
+ ports:
+ - name: http
+ port: 7878
+ targetPort: http
+ protocol: TCP
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: radarr-metrics
+ namespace: media-apps
+spec:
+ type: ClusterIP
+ selector:
+ app: radarr
+ ports:
+ - name: metrics
+ port: 9707
+ targetPort: metrics
+ protocol: TCP
diff --git a/apps/base/radarr/vaultstaticsecret.yaml b/apps/base/radarr/vaultstaticsecret.yaml
new file mode 100644
index 0000000..6b1cfc4
--- /dev/null
+++ b/apps/base/radarr/vaultstaticsecret.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: secrets.hashicorp.com/v1beta1
+kind: VaultStaticSecret
+metadata:
+ name: radarr
+ namespace: media-apps
+spec:
+ destination:
+ create: true
+ name: radarr-secrets
+ mount: kv
+ path: service/media-apps/radarr
+ refreshAfter: 30s
+ type: kv-v2
+ vaultAuthRef: default
+ rolloutRestartTargets:
+ - kind: Deployment
+ name: radarr
diff --git a/apps/base/sonarr/configmap.yaml b/apps/base/sonarr/configmap.yaml
new file mode 100644
index 0000000..820b2fc
--- /dev/null
+++ b/apps/base/sonarr/configmap.yaml
@@ -0,0 +1,25 @@
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: sonarr-config-xml
+ namespace: media-apps
+data:
+ config.xml: |
+
+ *
+ 8989
+ 9898
+ False
+ True
+ {{API_KEY}}
+ External
+ Enabled
+ info
+
+
+
+ Sonarr
+ Docker
+ False
+
diff --git a/apps/base/sonarr/deployment.yaml b/apps/base/sonarr/deployment.yaml
new file mode 100644
index 0000000..c8ebbfd
--- /dev/null
+++ b/apps/base/sonarr/deployment.yaml
@@ -0,0 +1,139 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: sonarr
+ namespace: media-apps
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ app: sonarr
+ template:
+ metadata:
+ labels:
+ app: sonarr
+ spec:
+ securityContext:
+ fsGroup: 1000
+ initContainers:
+ - name: config-template
+ image: busybox:latest
+ imagePullPolicy: IfNotPresent
+ command: ["/bin/sh"]
+ args:
+ - -c
+ - |
+ cp /config-template/config.xml /config/config.xml
+ sed -i "s/{{API_KEY}}/$API_KEY/g" /config/config.xml
+ env:
+ - name: API_KEY
+ valueFrom:
+ secretKeyRef:
+ name: sonarr-secrets
+ key: apitoken
+ securityContext:
+ runAsUser: 1000
+ runAsGroup: 1000
+ runAsNonRoot: true
+ volumeMounts:
+ - name: config-template
+ mountPath: /config-template
+ - name: config
+ mountPath: /config
+ containers:
+ - name: sonarr
+ image: ghcr.io/home-operations/sonarr:rolling
+ imagePullPolicy: IfNotPresent
+ ports:
+ - name: http
+ containerPort: 8989
+ protocol: TCP
+ env:
+ - name: TZ
+ value: Australia/Sydney
+ - name: PORT
+ value: "8989"
+ resources:
+ requests:
+ cpu: 50m
+ memory: 128Mi
+ limits:
+ cpu: "1"
+ memory: 1Gi
+ livenessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 30
+ periodSeconds: 30
+ readinessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ volumeMounts:
+ - name: config
+ mountPath: /config
+ - name: tvseries
+ mountPath: /mnt/tvseries
+ - name: exportarr
+ image: ghcr.io/onedr0p/exportarr:latest
+ imagePullPolicy: IfNotPresent
+ args: ["sonarr"]
+ ports:
+ - name: metrics
+ containerPort: 9707
+ protocol: TCP
+ env:
+ - name: TZ
+ value: Australia/Sydney
+ - name: PORT
+ value: "9707"
+ - name: URL
+ value: "http://localhost:8989"
+ - name: API_KEY
+ valueFrom:
+ secretKeyRef:
+ name: sonarr-secrets
+ key: apitoken
+ resources:
+ requests:
+ cpu: 50m
+ memory: 128Mi
+ limits:
+ cpu: 250m
+ memory: 256Mi
+ securityContext:
+ runAsUser: 1000
+ runAsGroup: 1000
+ runAsNonRoot: true
+ livenessProbe:
+ httpGet:
+ path: /metrics
+ port: metrics
+ initialDelaySeconds: 30
+ periodSeconds: 30
+ readinessProbe:
+ httpGet:
+ path: /metrics
+ port: metrics
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ volumeMounts:
+ - name: config
+ mountPath: /config
+ readOnly: true
+ volumes:
+ - name: config
+ persistentVolumeClaim:
+ claimName: sonarr-config
+ - name: config-template
+ configMap:
+ name: sonarr-config-xml
+ - name: tvseries
+ persistentVolumeClaim:
+ claimName: tvseries
diff --git a/apps/base/sonarr/gateway.yaml b/apps/base/sonarr/gateway.yaml
new file mode 100644
index 0000000..0a53e06
--- /dev/null
+++ b/apps/base/sonarr/gateway.yaml
@@ -0,0 +1,37 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ labels:
+ traefik.io/instance: internal
+ annotations:
+ cert-manager.io/cluster-issuer: vault-issuer
+ cert-manager.io/common-name: sonarr.k8s.syd1.au.unkin.net
+ cert-manager.io/private-key-size: "4096"
+ external-dns.alpha.kubernetes.io/hostname: sonarr.k8s.syd1.au.unkin.net
+ external-dns.alpha.kubernetes.io/target: 198.18.200.4
+ name: sonarr
+ namespace: media-apps
+spec:
+ gatewayClassName: traefik-internal
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: sonarr.k8s.syd1.au.unkin.net
+ name: http
+ port: 80
+ protocol: HTTP
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ hostname: sonarr.k8s.syd1.au.unkin.net
+ name: https
+ port: 443
+ protocol: HTTPS
+ tls:
+ certificateRefs:
+ - group: ""
+ kind: Secret
+ name: sonarr-tls
+ mode: Terminate
diff --git a/apps/base/sonarr/httproute.yaml b/apps/base/sonarr/httproute.yaml
new file mode 100644
index 0000000..8ab7ddc
--- /dev/null
+++ b/apps/base/sonarr/httproute.yaml
@@ -0,0 +1,49 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: http-redirect
+ namespace: media-apps
+spec:
+ hostnames:
+ - sonarr.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: sonarr
+ sectionName: http
+ rules:
+ - filters:
+ - type: RequestRedirect
+ requestRedirect:
+ scheme: https
+ statusCode: 301
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: sonarr
+ namespace: media-apps
+spec:
+ hostnames:
+ - sonarr.k8s.syd1.au.unkin.net
+ parentRefs:
+ - group: gateway.networking.k8s.io
+ kind: Gateway
+ name: sonarr
+ sectionName: https
+ rules:
+ - backendRefs:
+ - group: ""
+ kind: Service
+ name: sonarr
+ port: 8989
+ weight: 1
+ matches:
+ - path:
+ type: PathPrefix
+ value: /
diff --git a/apps/base/sonarr/kustomization.yaml b/apps/base/sonarr/kustomization.yaml
new file mode 100644
index 0000000..7552699
--- /dev/null
+++ b/apps/base/sonarr/kustomization.yaml
@@ -0,0 +1,12 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - pvc.yaml
+ - configmap.yaml
+ - vaultstaticsecret.yaml
+ - deployment.yaml
+ - service.yaml
+ - gateway.yaml
+ - httproute.yaml
diff --git a/apps/base/sonarr/pvc.yaml b/apps/base/sonarr/pvc.yaml
new file mode 100644
index 0000000..2d6d1b5
--- /dev/null
+++ b/apps/base/sonarr/pvc.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: sonarr-config
+ namespace: media-apps
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+ storageClassName: cephrbd-fast-delete
+ volumeMode: Filesystem
diff --git a/apps/base/sonarr/service.yaml b/apps/base/sonarr/service.yaml
new file mode 100644
index 0000000..e829137
--- /dev/null
+++ b/apps/base/sonarr/service.yaml
@@ -0,0 +1,30 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: sonarr
+ namespace: media-apps
+spec:
+ type: ClusterIP
+ selector:
+ app: sonarr
+ ports:
+ - name: http
+ port: 8989
+ targetPort: http
+ protocol: TCP
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: sonarr-metrics
+ namespace: media-apps
+spec:
+ type: ClusterIP
+ selector:
+ app: sonarr
+ ports:
+ - name: metrics
+ port: 9707
+ targetPort: metrics
+ protocol: TCP
diff --git a/apps/base/sonarr/vaultstaticsecret.yaml b/apps/base/sonarr/vaultstaticsecret.yaml
new file mode 100644
index 0000000..596f1c6
--- /dev/null
+++ b/apps/base/sonarr/vaultstaticsecret.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: secrets.hashicorp.com/v1beta1
+kind: VaultStaticSecret
+metadata:
+ name: sonarr
+ namespace: media-apps
+spec:
+ destination:
+ create: true
+ name: sonarr-secrets
+ mount: kv
+ path: service/media-apps/sonarr
+ refreshAfter: 30s
+ type: kv-v2
+ vaultAuthRef: default
+ rolloutRestartTargets:
+ - kind: Deployment
+ name: sonarr
diff --git a/apps/overlays/au-syd1/bazarr/kustomization.yaml b/apps/overlays/au-syd1/bazarr/kustomization.yaml
new file mode 100644
index 0000000..7a4eb40
--- /dev/null
+++ b/apps/overlays/au-syd1/bazarr/kustomization.yaml
@@ -0,0 +1,6 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - ../../../base/bazarr
diff --git a/apps/overlays/au-syd1/jellyfin/kustomization.yaml b/apps/overlays/au-syd1/jellyfin/kustomization.yaml
new file mode 100644
index 0000000..d8d5df6
--- /dev/null
+++ b/apps/overlays/au-syd1/jellyfin/kustomization.yaml
@@ -0,0 +1,6 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - ../../../base/jellyfin
diff --git a/apps/overlays/au-syd1/jellyseerr/kustomization.yaml b/apps/overlays/au-syd1/jellyseerr/kustomization.yaml
new file mode 100644
index 0000000..daf1683
--- /dev/null
+++ b/apps/overlays/au-syd1/jellyseerr/kustomization.yaml
@@ -0,0 +1,6 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - ../../../base/jellyseerr
diff --git a/apps/overlays/au-syd1/media-apps/kustomization.yaml b/apps/overlays/au-syd1/media-apps/kustomization.yaml
new file mode 100644
index 0000000..68cf968
--- /dev/null
+++ b/apps/overlays/au-syd1/media-apps/kustomization.yaml
@@ -0,0 +1,6 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - ../../../base/media-apps
diff --git a/apps/overlays/au-syd1/nzbget/kustomization.yaml b/apps/overlays/au-syd1/nzbget/kustomization.yaml
new file mode 100644
index 0000000..8f2c9ae
--- /dev/null
+++ b/apps/overlays/au-syd1/nzbget/kustomization.yaml
@@ -0,0 +1,6 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - ../../../base/nzbget
diff --git a/apps/overlays/au-syd1/prowlarr/kustomization.yaml b/apps/overlays/au-syd1/prowlarr/kustomization.yaml
new file mode 100644
index 0000000..c2b0fc3
--- /dev/null
+++ b/apps/overlays/au-syd1/prowlarr/kustomization.yaml
@@ -0,0 +1,6 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - ../../../base/prowlarr
diff --git a/apps/overlays/au-syd1/radarr/kustomization.yaml b/apps/overlays/au-syd1/radarr/kustomization.yaml
new file mode 100644
index 0000000..be3218a
--- /dev/null
+++ b/apps/overlays/au-syd1/radarr/kustomization.yaml
@@ -0,0 +1,6 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - ../../../base/radarr
diff --git a/apps/overlays/au-syd1/sonarr/kustomization.yaml b/apps/overlays/au-syd1/sonarr/kustomization.yaml
new file mode 100644
index 0000000..9e686f0
--- /dev/null
+++ b/apps/overlays/au-syd1/sonarr/kustomization.yaml
@@ -0,0 +1,6 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - ../../../base/sonarr
diff --git a/argocd/applicationsets/kustomization.yaml b/argocd/applicationsets/kustomization.yaml
index b54cc35..c95dedd 100644
--- a/argocd/applicationsets/kustomization.yaml
+++ b/argocd/applicationsets/kustomization.yaml
@@ -5,6 +5,7 @@ kind: Kustomization
resources:
- aitooling.yaml
- logging.yaml
+ - media.yaml
- observability.yaml
- platform.yaml
- storage.yaml
diff --git a/argocd/applicationsets/media.yaml b/argocd/applicationsets/media.yaml
new file mode 100644
index 0000000..f1e5955
--- /dev/null
+++ b/argocd/applicationsets/media.yaml
@@ -0,0 +1,41 @@
+---
+apiVersion: argoproj.io/v1alpha1
+kind: ApplicationSet
+metadata:
+ name: media-apps
+ namespace: argocd
+spec:
+ generators:
+ - git:
+ repoURL: https://git.unkin.net/unkin/argocd-apps
+ revision: HEAD
+ directories:
+ # Shared foundation: media-apps namespace, vault-reader SA, VaultAuth,
+ # and the RWX movies/tvseries library PVCs the whole stack mounts.
+ - path: apps/overlays/*/media-apps
+ - path: apps/overlays/*/jellyfin
+ - path: apps/overlays/*/sonarr
+ - path: apps/overlays/*/radarr
+ - path: apps/overlays/*/prowlarr
+ - path: apps/overlays/*/bazarr
+ - path: apps/overlays/*/nzbget
+ - path: apps/overlays/*/jellyseerr
+ template:
+ metadata:
+ name: 'media-{{path[3]}}'
+ spec:
+ project: media
+ source:
+ repoURL: https://git.unkin.net/unkin/argocd-apps
+ targetRevision: HEAD
+ path: '{{path}}'
+ destination:
+ server: https://kubernetes.default.svc
+ # All media apps share the media-apps namespace (not the dir name).
+ namespace: media-apps
+ syncPolicy:
+ automated:
+ prune: true
+ selfHeal: true
+ syncOptions:
+ - ServerSideApply=true
diff --git a/argocd/projects/kustomization.yaml b/argocd/projects/kustomization.yaml
index b54cc35..c95dedd 100644
--- a/argocd/projects/kustomization.yaml
+++ b/argocd/projects/kustomization.yaml
@@ -5,6 +5,7 @@ kind: Kustomization
resources:
- aitooling.yaml
- logging.yaml
+ - media.yaml
- observability.yaml
- platform.yaml
- storage.yaml
diff --git a/argocd/projects/media.yaml b/argocd/projects/media.yaml
new file mode 100644
index 0000000..040392d
--- /dev/null
+++ b/argocd/projects/media.yaml
@@ -0,0 +1,19 @@
+---
+apiVersion: argoproj.io/v1alpha1
+kind: AppProject
+metadata:
+ name: media
+ namespace: argocd
+spec:
+ description: Media services (jellyfin + *arr stack)
+ sourceRepos:
+ - https://git.unkin.net/unkin/argocd-apps
+ destinations:
+ - namespace: media-apps
+ server: https://kubernetes.default.svc
+ clusterResourceWhitelist:
+ - group: ''
+ kind: Namespace
+ namespaceResourceWhitelist:
+ - group: '*'
+ kind: '*'