From e03aeca101e9502cdcb7473a8933f09f89dce84e Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 9 Aug 2026 13:25:25 +1000 Subject: [PATCH] Add media-apps stack to ArgoCD Why: The media stack (jellyfin plus the sonarr/radarr/prowlarr/bazarr/nzbget/ jellyseerr apps) runs in the media-apps namespace but is deployed out-of-band by terraform-k8s rather than GitOps. Bringing it under ArgoCD makes the stack declarative, self-healing, and consistent with every other cluster workload, and prepares terraform-k8s to drop the media-apps config. How: - Add a media AppProject scoped to the media-apps namespace and a media-apps ApplicationSet that renders one Application per app plus a shared foundation. - Add a shared media-apps foundation (namespace, media-apps-vault-reader ServiceAccount, default VaultAuth on k8s/au/syd1, and the RWX movies/tvseries library PVCs) that the whole stack mounts. - Add per-app kustomize base and au-syd1 overlay for jellyfin and the six *arr apps, using plain resource names (jellyfin, sonarr, ...) with fresh PVCs. - Deploy jellyfin from the jellyfin-ha fork (Redis transcode store, RWX transcode scratch) wired to the shared movies/tvseries library PVCs, keeping the intel iGPU nodeSelector and gpu.intel.com/i915 request. - Source API keys and nzbget credentials through VSO VaultStaticSecrets from kv/service/media-apps/; expose each app via a traefik-internal Gateway and HTTPRoute at .k8s.syd1.au.unkin.net. - Register the media project and applicationset in the argocd bootstrap kustomizations. --- apps/base/bazarr/deployment.yaml | 61 ++++++++ apps/base/bazarr/gateway.yaml | 37 +++++ apps/base/bazarr/httproute.yaml | 49 ++++++ apps/base/bazarr/kustomization.yaml | 10 ++ apps/base/bazarr/pvc.yaml | 14 ++ apps/base/bazarr/service.yaml | 15 ++ apps/base/jellyfin/deployment.yaml | 98 ++++++++++++ apps/base/jellyfin/gateway.yaml | 37 +++++ apps/base/jellyfin/httproute.yaml | 49 ++++++ apps/base/jellyfin/kustomization.yaml | 15 ++ apps/base/jellyfin/pvc_cache.yaml | 15 ++ apps/base/jellyfin/pvc_config.yaml | 15 ++ apps/base/jellyfin/pvc_transcode.yaml | 17 +++ apps/base/jellyfin/redis-deployment.yaml | 64 ++++++++ apps/base/jellyfin/redis-pvc.yaml | 14 ++ apps/base/jellyfin/redis-service.yaml | 17 +++ apps/base/jellyfin/service.yaml | 17 +++ apps/base/jellyseerr/deployment.yaml | 61 ++++++++ apps/base/jellyseerr/gateway.yaml | 37 +++++ apps/base/jellyseerr/httproute.yaml | 49 ++++++ apps/base/jellyseerr/kustomization.yaml | 10 ++ apps/base/jellyseerr/pvc.yaml | 14 ++ apps/base/jellyseerr/service.yaml | 15 ++ apps/base/media-apps/kustomization.yaml | 10 ++ apps/base/media-apps/namespace.yaml | 5 + apps/base/media-apps/pvc_movies.yaml | 16 ++ apps/base/media-apps/pvc_tvseries.yaml | 16 ++ apps/base/media-apps/serviceaccount.yaml | 12 ++ apps/base/media-apps/vaultauth.yaml | 18 +++ apps/base/nzbget/deployment.yaml | 81 ++++++++++ apps/base/nzbget/gateway.yaml | 37 +++++ apps/base/nzbget/httproute.yaml | 49 ++++++ apps/base/nzbget/kustomization.yaml | 11 ++ apps/base/nzbget/pvc.yaml | 14 ++ apps/base/nzbget/service.yaml | 15 ++ apps/base/nzbget/vaultstaticsecret.yaml | 18 +++ apps/base/prowlarr/configmap.yaml | 25 ++++ apps/base/prowlarr/deployment.yaml | 136 +++++++++++++++++ apps/base/prowlarr/gateway.yaml | 37 +++++ apps/base/prowlarr/httproute.yaml | 49 ++++++ apps/base/prowlarr/kustomization.yaml | 12 ++ apps/base/prowlarr/pvc.yaml | 14 ++ apps/base/prowlarr/service.yaml | 30 ++++ apps/base/prowlarr/vaultstaticsecret.yaml | 18 +++ apps/base/radarr/configmap.yaml | 25 ++++ apps/base/radarr/deployment.yaml | 139 ++++++++++++++++++ apps/base/radarr/gateway.yaml | 37 +++++ apps/base/radarr/httproute.yaml | 49 ++++++ apps/base/radarr/kustomization.yaml | 12 ++ apps/base/radarr/pvc.yaml | 14 ++ apps/base/radarr/service.yaml | 30 ++++ apps/base/radarr/vaultstaticsecret.yaml | 18 +++ apps/base/sonarr/configmap.yaml | 25 ++++ apps/base/sonarr/deployment.yaml | 139 ++++++++++++++++++ apps/base/sonarr/gateway.yaml | 37 +++++ apps/base/sonarr/httproute.yaml | 49 ++++++ apps/base/sonarr/kustomization.yaml | 12 ++ apps/base/sonarr/pvc.yaml | 14 ++ apps/base/sonarr/service.yaml | 30 ++++ apps/base/sonarr/vaultstaticsecret.yaml | 18 +++ .../au-syd1/bazarr/kustomization.yaml | 6 + .../au-syd1/jellyfin/kustomization.yaml | 6 + .../au-syd1/jellyseerr/kustomization.yaml | 6 + .../au-syd1/media-apps/kustomization.yaml | 6 + .../au-syd1/nzbget/kustomization.yaml | 6 + .../au-syd1/prowlarr/kustomization.yaml | 6 + .../au-syd1/radarr/kustomization.yaml | 6 + .../au-syd1/sonarr/kustomization.yaml | 6 + argocd/applicationsets/kustomization.yaml | 1 + argocd/applicationsets/media.yaml | 41 ++++++ argocd/projects/kustomization.yaml | 1 + argocd/projects/media.yaml | 19 +++ 72 files changed, 2111 insertions(+) create mode 100644 apps/base/bazarr/deployment.yaml create mode 100644 apps/base/bazarr/gateway.yaml create mode 100644 apps/base/bazarr/httproute.yaml create mode 100644 apps/base/bazarr/kustomization.yaml create mode 100644 apps/base/bazarr/pvc.yaml create mode 100644 apps/base/bazarr/service.yaml create mode 100644 apps/base/jellyfin/deployment.yaml create mode 100644 apps/base/jellyfin/gateway.yaml create mode 100644 apps/base/jellyfin/httproute.yaml create mode 100644 apps/base/jellyfin/kustomization.yaml create mode 100644 apps/base/jellyfin/pvc_cache.yaml create mode 100644 apps/base/jellyfin/pvc_config.yaml create mode 100644 apps/base/jellyfin/pvc_transcode.yaml create mode 100644 apps/base/jellyfin/redis-deployment.yaml create mode 100644 apps/base/jellyfin/redis-pvc.yaml create mode 100644 apps/base/jellyfin/redis-service.yaml create mode 100644 apps/base/jellyfin/service.yaml create mode 100644 apps/base/jellyseerr/deployment.yaml create mode 100644 apps/base/jellyseerr/gateway.yaml create mode 100644 apps/base/jellyseerr/httproute.yaml create mode 100644 apps/base/jellyseerr/kustomization.yaml create mode 100644 apps/base/jellyseerr/pvc.yaml create mode 100644 apps/base/jellyseerr/service.yaml create mode 100644 apps/base/media-apps/kustomization.yaml create mode 100644 apps/base/media-apps/namespace.yaml create mode 100644 apps/base/media-apps/pvc_movies.yaml create mode 100644 apps/base/media-apps/pvc_tvseries.yaml create mode 100644 apps/base/media-apps/serviceaccount.yaml create mode 100644 apps/base/media-apps/vaultauth.yaml create mode 100644 apps/base/nzbget/deployment.yaml create mode 100644 apps/base/nzbget/gateway.yaml create mode 100644 apps/base/nzbget/httproute.yaml create mode 100644 apps/base/nzbget/kustomization.yaml create mode 100644 apps/base/nzbget/pvc.yaml create mode 100644 apps/base/nzbget/service.yaml create mode 100644 apps/base/nzbget/vaultstaticsecret.yaml create mode 100644 apps/base/prowlarr/configmap.yaml create mode 100644 apps/base/prowlarr/deployment.yaml create mode 100644 apps/base/prowlarr/gateway.yaml create mode 100644 apps/base/prowlarr/httproute.yaml create mode 100644 apps/base/prowlarr/kustomization.yaml create mode 100644 apps/base/prowlarr/pvc.yaml create mode 100644 apps/base/prowlarr/service.yaml create mode 100644 apps/base/prowlarr/vaultstaticsecret.yaml create mode 100644 apps/base/radarr/configmap.yaml create mode 100644 apps/base/radarr/deployment.yaml create mode 100644 apps/base/radarr/gateway.yaml create mode 100644 apps/base/radarr/httproute.yaml create mode 100644 apps/base/radarr/kustomization.yaml create mode 100644 apps/base/radarr/pvc.yaml create mode 100644 apps/base/radarr/service.yaml create mode 100644 apps/base/radarr/vaultstaticsecret.yaml create mode 100644 apps/base/sonarr/configmap.yaml create mode 100644 apps/base/sonarr/deployment.yaml create mode 100644 apps/base/sonarr/gateway.yaml create mode 100644 apps/base/sonarr/httproute.yaml create mode 100644 apps/base/sonarr/kustomization.yaml create mode 100644 apps/base/sonarr/pvc.yaml create mode 100644 apps/base/sonarr/service.yaml create mode 100644 apps/base/sonarr/vaultstaticsecret.yaml create mode 100644 apps/overlays/au-syd1/bazarr/kustomization.yaml create mode 100644 apps/overlays/au-syd1/jellyfin/kustomization.yaml create mode 100644 apps/overlays/au-syd1/jellyseerr/kustomization.yaml create mode 100644 apps/overlays/au-syd1/media-apps/kustomization.yaml create mode 100644 apps/overlays/au-syd1/nzbget/kustomization.yaml create mode 100644 apps/overlays/au-syd1/prowlarr/kustomization.yaml create mode 100644 apps/overlays/au-syd1/radarr/kustomization.yaml create mode 100644 apps/overlays/au-syd1/sonarr/kustomization.yaml create mode 100644 argocd/applicationsets/media.yaml create mode 100644 argocd/projects/media.yaml 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: '*'