{ globals, config, lib, utils, ... }: { options.media.enable = lib.mkEnableOption "media"; config = lib.mkIf config.media.enable { kubernetes.resources = { deployments = { jellyfin.spec = { selector.matchLabels = { app = "media"; component = "jellyfin"; }; strategy = { type = "RollingUpdate"; rollingUpdate = { maxSurge = 0; maxUnavailable = 1; }; }; template = { metadata.labels = { app = "media"; component = "jellyfin"; }; spec = { containers.jellyfin = { image = globals.images.jellyfin; ports.web.containerPort = 8096; imagePullPolicy = "IfNotPresent"; env.JELLYFIN_PublishedServerUrl.value = "https://media.kun.is"; volumeMounts = [ { name = "config"; mountPath = "/config"; } { name = "media"; mountPath = "/media"; } { name = "cache"; mountPath = "/config/transcodes"; } ]; }; volumes = { config.persistentVolumeClaim.claimName = "jellyfin"; cache.persistentVolumeClaim.claimName = "jellyfin-cache"; media.hostPath = { path = "/mnt/longhorn/persistent/media"; type = "Directory"; }; }; securityContext = { fsGroup = 0; fsGroupChangePolicy = "OnRootMismatch"; }; affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms = [ { matchExpressions = [ { key = "hasMedia"; operator = "In"; values = ["true"]; } ]; } ]; }; }; }; deluge.spec = { selector.matchLabels = { app = "media"; component = "deluge"; }; strategy = { type = "RollingUpdate"; rollingUpdate = { maxSurge = 0; maxUnavailable = 1; }; }; template = { metadata.labels = { app = "media"; component = "deluge"; }; spec = { containers.deluge = { image = globals.images.deluge; imagePullPolicy = "IfNotPresent"; env = { PUID.value = "1000"; PGID.value = "1000"; TZ.value = "Europe/Amsterdam"; DELUGE_LOGLEVEL.value = "info"; }; ports = { web.containerPort = 8112; bittorrent.containerPort = 31780; }; volumeMounts = [ { name = "config"; mountPath = "/config"; } { name = "media"; mountPath = "/media"; } ]; }; volumes = { config.persistentVolumeClaim.claimName = "deluge"; media.persistentVolumeClaim.claimName = "media"; }; }; }; }; jellyseerr.spec = { selector.matchLabels = { app = "media"; component = "jellyseerr"; }; strategy = { type = "RollingUpdate"; rollingUpdate = { maxSurge = 0; maxUnavailable = 1; }; }; template = { metadata.labels = { app = "media"; component = "jellyseerr"; }; spec = { volumes.config.persistentVolumeClaim.claimName = "jellyseerr"; containers.jellyseerr = { image = utils.mkNixNGImage "jellyseerr"; ports.web.containerPort = 5055; imagePullPolicy = "IfNotPresent"; env = { LOG_LEVEL.value = "debug"; TZ.value = "Europe/Amsterdam"; }; volumeMounts = [ { name = "config"; mountPath = "/app/config"; } ]; }; securityContext = { fsGroup = 0; fsGroupChangePolicy = "OnRootMismatch"; }; }; }; }; radarr.spec = { selector.matchLabels = { app = "media"; component = "radarr"; }; strategy = { type = "RollingUpdate"; rollingUpdate = { maxSurge = 0; maxUnavailable = 1; }; }; template = { metadata.labels = { app = "media"; component = "radarr"; }; spec = { containers.radarr = { image = globals.images.radarr; ports.web.containerPort = 7878; imagePullPolicy = "IfNotPresent"; env = { PUID.value = "1000"; PGID.value = "1000"; TZ.value = "Europe/Amsterdam"; }; volumeMounts = [ { name = "config"; mountPath = "/config"; } { name = "media"; mountPath = "/media"; } ]; }; volumes = { config.persistentVolumeClaim.claimName = "radarr"; media.persistentVolumeClaim.claimName = "media"; }; securityContext = { fsGroup = 1000; fsGroupChangePolicy = "OnRootMismatch"; }; }; }; }; prowlarr.spec = { selector.matchLabels = { app = "media"; component = "prowlarr"; }; strategy = { type = "RollingUpdate"; rollingUpdate = { maxSurge = 0; maxUnavailable = 1; }; }; template = { metadata.labels = { app = "media"; component = "prowlarr"; }; spec = { volumes.config.persistentVolumeClaim.claimName = "prowlarr"; containers.prowlarr = { image = globals.images.prowlarr; ports.web.containerPort = 9696; imagePullPolicy = "IfNotPresent"; env = { PUID.value = "1000"; PGID.value = "1000"; TZ.value = "Europe/Amsterdam"; }; volumeMounts = [ { name = "config"; mountPath = "/config"; } ]; }; securityContext = { fsGroup = 1000; fsGroupChangePolicy = "OnRootMismatch"; }; }; }; }; sonarr.spec = { selector.matchLabels = { app = "media"; component = "sonarr"; }; strategy = { type = "RollingUpdate"; rollingUpdate = { maxSurge = 0; maxUnavailable = 1; }; }; template = { metadata.labels = { app = "media"; component = "sonarr"; }; spec = { containers.sonarr = { image = globals.images.sonarr; ports.web.containerPort = 8989; imagePullPolicy = "IfNotPresent"; env = { PUID.value = "1000"; PGID.value = "1000"; TZ.value = "Europe/Amsterdam"; }; volumeMounts = [ { name = "config"; mountPath = "/config"; } { name = "media"; mountPath = "/media"; } ]; }; volumes = { config.persistentVolumeClaim.claimName = "sonarr"; media.persistentVolumeClaim.claimName = "media"; }; securityContext = { fsGroup = 1000; fsGroupChangePolicy = "OnRootMismatch"; }; }; }; }; bazarr.spec = { selector.matchLabels = { app = "media"; component = "bazarr"; }; strategy = { type = "RollingUpdate"; rollingUpdate = { maxSurge = 0; maxUnavailable = 1; }; }; template = { metadata.labels = { app = "media"; component = "bazarr"; }; spec = { containers.bazarr = { image = globals.images.bazarr; ports.web.containerPort = 6767; imagePullPolicy = "IfNotPresent"; env = { PUID.value = "1000"; PGID.value = "1000"; TZ.value = "Europe/Amsterdam"; }; volumeMounts = [ { name = "config"; mountPath = "/config"; } { name = "media"; mountPath = "/media"; } ]; }; volumes = { config.persistentVolumeClaim.claimName = "bazarr"; media.persistentVolumeClaim.claimName = "media"; }; securityContext = { fsGroup = 1000; fsGroupChangePolicy = "OnRootMismatch"; }; }; }; }; }; services = { jellyfin.spec = { selector = { app = "media"; component = "jellyfin"; }; ports.web = { port = 80; targetPort = "web"; }; }; deluge.spec = { type = "LoadBalancer"; loadBalancerIP = globals.transmissionIPv4; selector = { app = "media"; component = "deluge"; }; ports = { bittorrent = { port = 31780; targetPort = "bittorrent"; }; web = { port = 80; targetPort = "web"; }; }; }; jellyseerr.spec = { type = "LoadBalancer"; loadBalancerIP = globals.jellyseerrIPv4; selector = { app = "media"; component = "jellyseerr"; }; ports.web = { port = 80; targetPort = "web"; }; }; radarr.spec = { type = "LoadBalancer"; loadBalancerIP = globals.radarrIPv4; selector = { app = "media"; component = "radarr"; }; ports.web = { port = 80; targetPort = "web"; }; }; prowlarr.spec = { type = "LoadBalancer"; loadBalancerIP = globals.prowlarrIPv4; selector = { app = "media"; component = "prowlarr"; }; ports.web = { port = 80; targetPort = "web"; }; }; sonarr.spec = { type = "LoadBalancer"; loadBalancerIP = globals.sonarrIPv4; selector = { app = "media"; component = "sonarr"; }; ports.web = { port = 80; targetPort = "web"; }; }; bazarr.spec = { type = "LoadBalancer"; loadBalancerIP = globals.bazarrIPv4; selector = { app = "media"; component = "bazarr"; }; ports.web = { port = 80; targetPort = "web"; }; }; }; persistentVolumeClaims = { jellyfin-cache.spec = { accessModes = ["ReadWriteOnce"]; resources.requests.storage = "20Gi"; }; media.spec = { accessModes = ["ReadWriteMany"]; storageClassName = ""; resources.requests.storage = "1Mi"; volumeName = "media-media"; }; }; }; lab = { ingresses = { jellyfin = { host = "media.kun.is"; service = { name = "jellyfin"; portName = "web"; }; }; jellyseerr = { host = "jellyseerr.kun.is"; entrypoint = "localsecure"; service = { name = "jellyseerr"; portName = "web"; }; }; }; tailscaleIngresses = { tailscale-jellyseerr = { host = "jellyseerr"; service.name = "jellyseerr"; }; tailscale-radarr = { host = "radarr"; service.name = "radarr"; }; tailscale-sonarr = { host = "sonarr"; service.name = "sonarr"; }; tailscale-bazarr = { host = "bazarr"; service.name = "bazarr"; }; tailscale-prowlarr = { host = "prowlarr"; service.name = "prowlarr"; }; tailscale-deluge = { host = "deluge"; service.name = "deluge"; }; }; longhorn.persistentVolumeClaim = { jellyfin = { volumeName = "jellyfin"; storage = "5Gi"; }; deluge = { volumeName = "deluge"; storage = "500Mi"; }; jellyseerr = { volumeName = "jellyseerr"; storage = "75Mi"; }; radarr = { volumeName = "radarr"; storage = "300Mi"; }; prowlarr = { volumeName = "prowlarr"; storage = "150Mi"; }; sonarr = { volumeName = "sonarr"; storage = "150Mi"; }; bazarr = { volumeName = "bazarr"; storage = "25Mi"; }; }; }; }; }