From 0c75d07f73582ae4b192e0c85ddc0a427d4baab9 Mon Sep 17 00:00:00 2001 From: Pim Kunis Date: Sat, 18 Jan 2025 16:20:24 +0100 Subject: [PATCH] Add deployment options for media kubenix manifest Cleanup sonarr log files Increase size of sonarr volume --- modules/bootstrap-default.nix | 2 +- modules/media.nix | 877 ++++++++++++++++++---------------- nixng-modules/sonarr.nix | 3 +- 3 files changed, 462 insertions(+), 420 deletions(-) diff --git a/modules/bootstrap-default.nix b/modules/bootstrap-default.nix index edc12e8..e578fa6 100644 --- a/modules/bootstrap-default.nix +++ b/modules/bootstrap-default.nix @@ -132,7 +132,7 @@ jellyseerr.storage = "75Mi"; radarr.storage = "300Mi"; prowlarr.storage = "150Mi"; - sonarr.storage = "150Mi"; + sonarr.storage = "250Mi"; bazarr.storage = "25Mi"; minecraft.storage = "1Gi"; ntfy.storage = "300Mi"; diff --git a/modules/media.nix b/modules/media.nix index b70402e..13f64b2 100644 --- a/modules/media.nix +++ b/modules/media.nix @@ -4,421 +4,446 @@ lib, utils, ... -}: { - options.media.enable = lib.mkEnableOption "media"; +}: let + cfg = config.media; +in { + options.media = { + enable = lib.mkEnableOption "media"; + jellyfin.enable = (lib.mkEnableOption "jellyfin") // {default = true;}; + deluge.enable = (lib.mkEnableOption "deluge") // {default = true;}; + jellyseerr.enable = (lib.mkEnableOption "jellyseerr") // {default = true;}; + radarr.enable = (lib.mkEnableOption "radarr") // {default = true;}; + prowlarr.enable = (lib.mkEnableOption "prowlarr") // {default = true;}; + sonarr.enable = (lib.mkEnableOption "sonarr") // {default = true;}; + bazarr.enable = (lib.mkEnableOption "bazarr") // {default = true;}; + }; - config = lib.mkIf config.media.enable { + config = lib.mkIf cfg.enable { kubernetes.resources = { deployments = { - jellyfin.spec = { - selector.matchLabels = { - app = "media"; - component = "jellyfin"; - }; - - strategy = { - type = "RollingUpdate"; - - rollingUpdate = { - maxSurge = 0; - maxUnavailable = 1; - }; - }; - - template = { - metadata.labels = { + jellyfin = lib.mkIf cfg.jellyfin.enable { + spec = { + selector.matchLabels = { app = "media"; component = "jellyfin"; }; - spec = { - containers.jellyfin = { - image = globals.images.jellyfin; - ports.web.containerPort = 8096; - imagePullPolicy = "IfNotPresent"; + strategy = { + type = "RollingUpdate"; - env.JELLYFIN_PublishedServerUrl.value = "https://media.kun.is"; + rollingUpdate = { + maxSurge = 0; + maxUnavailable = 1; + }; + }; - volumeMounts = [ + 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 = [ { - name = "config"; - mountPath = "/config"; - } - { - name = "media"; - mountPath = "/media"; - } - { - name = "cache"; - mountPath = "/config/transcodes"; + matchExpressions = [ + { + key = "hasMedia"; + operator = "In"; + values = ["true"]; + } + ]; } ]; }; - - 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 = { + deluge = lib.mkIf cfg.deluge.enable { + spec = { + selector.matchLabels = { app = "media"; component = "deluge"; }; - spec = { - containers.deluge = { - image = utils.mkNixNGImage "deluge"; - imagePullPolicy = "IfNotPresent"; + strategy = { + type = "RollingUpdate"; - env = { - PUID.value = "1000"; - PGID.value = "1000"; - TZ.value = "Europe/Amsterdam"; - DELUGE_LOGLEVEL.value = "info"; - }; + rollingUpdate = { + maxSurge = 0; + maxUnavailable = 1; + }; + }; - ports = { - web.containerPort = 8112; - bittorrent.containerPort = 31780; - }; - - volumeMounts = [ - { - name = "config"; - mountPath = "/config"; - } - { - name = "media"; - mountPath = "/media"; - } - ]; + template = { + metadata.labels = { + app = "media"; + component = "deluge"; }; - volumes = { - config.persistentVolumeClaim.claimName = "deluge"; - media.persistentVolumeClaim.claimName = "media"; - }; + spec = { + containers.deluge = { + image = utils.mkNixNGImage "deluge"; + imagePullPolicy = "IfNotPresent"; - securityContext = { - fsGroup = 51; - fsGroupChangePolicy = "OnRootMismatch"; + 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"; + }; + + securityContext = { + fsGroup = 51; + fsGroupChangePolicy = "OnRootMismatch"; + }; }; }; }; }; - jellyseerr.spec = { - selector.matchLabels = { - app = "media"; - component = "jellyseerr"; - }; - - strategy = { - type = "RollingUpdate"; - - rollingUpdate = { - maxSurge = 0; - maxUnavailable = 1; - }; - }; - - template = { - metadata.labels = { + jellyseerr = lib.mkIf cfg.jellyseerr.enable { + spec = { + selector.matchLabels = { app = "media"; component = "jellyseerr"; }; - spec = { - volumes.config.persistentVolumeClaim.claimName = "jellyseerr"; + strategy = { + type = "RollingUpdate"; - containers.jellyseerr = { - image = utils.mkNixNGImage "jellyseerr"; - ports.web.containerPort = 5055; - imagePullPolicy = "IfNotPresent"; + rollingUpdate = { + maxSurge = 0; + maxUnavailable = 1; + }; + }; - env = { - LOG_LEVEL.value = "debug"; - TZ.value = "Europe/Amsterdam"; - }; - - volumeMounts = [ - { - name = "config"; - mountPath = "/app/config"; - } - ]; + template = { + metadata.labels = { + app = "media"; + component = "jellyseerr"; }; - securityContext = { - # TODO: don't hardcode this - fsGroup = 51; - fsGroupChangePolicy = "OnRootMismatch"; + 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 = { + # TODO: don't hardcode this + fsGroup = 51; + fsGroupChangePolicy = "OnRootMismatch"; + }; }; }; }; }; - radarr.spec = { - selector.matchLabels = { - app = "media"; - component = "radarr"; - }; - - strategy = { - type = "RollingUpdate"; - - rollingUpdate = { - maxSurge = 0; - maxUnavailable = 1; - }; - }; - - template = { - metadata.labels = { + radarr = lib.mkIf cfg.radarr.enable { + spec = { + selector.matchLabels = { app = "media"; component = "radarr"; }; - spec = { - containers.radarr = { - image = utils.mkNixNGImage "radarr"; - ports.web.containerPort = 7878; - imagePullPolicy = "IfNotPresent"; + strategy = { + type = "RollingUpdate"; - env = { - PUID.value = "1000"; - PGID.value = "1000"; - TZ.value = "Europe/Amsterdam"; + rollingUpdate = { + maxSurge = 0; + maxUnavailable = 1; + }; + }; + + template = { + metadata.labels = { + app = "media"; + component = "radarr"; + }; + + spec = { + containers.radarr = { + image = utils.mkNixNGImage "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"; + } + ]; }; - volumeMounts = [ - { - name = "config"; - mountPath = "/config"; - } - { - name = "media"; - mountPath = "/media"; - } - ]; - }; + volumes = { + config.persistentVolumeClaim.claimName = "radarr"; + media.persistentVolumeClaim.claimName = "media"; + }; - volumes = { - config.persistentVolumeClaim.claimName = "radarr"; - media.persistentVolumeClaim.claimName = "media"; - }; - - securityContext = { - fsGroup = 410; - fsGroupChangePolicy = "OnRootMismatch"; + securityContext = { + fsGroup = 410; + fsGroupChangePolicy = "OnRootMismatch"; + }; }; }; }; }; - prowlarr.spec = { - selector.matchLabels = { - app = "media"; - component = "prowlarr"; - }; - - strategy = { - type = "RollingUpdate"; - - rollingUpdate = { - maxSurge = 0; - maxUnavailable = 1; - }; - }; - - template = { - metadata.labels = { + prowlarr = lib.mkIf cfg.prowlarr.enable { + spec = { + selector.matchLabels = { app = "media"; component = "prowlarr"; }; - spec = { - volumes.config.persistentVolumeClaim.claimName = "prowlarr"; + strategy = { + type = "RollingUpdate"; - containers.prowlarr = { - image = utils.mkNixNGImage "prowlarr"; - ports.web.containerPort = 9696; - imagePullPolicy = "IfNotPresent"; + rollingUpdate = { + maxSurge = 0; + maxUnavailable = 1; + }; + }; - env = { - PUID.value = "1000"; - PGID.value = "1000"; - TZ.value = "Europe/Amsterdam"; - }; - - volumeMounts = [ - { - name = "config"; - mountPath = "/config"; - } - ]; + template = { + metadata.labels = { + app = "media"; + component = "prowlarr"; }; - securityContext = { - fsGroup = 413; - fsGroupChangePolicy = "OnRootMismatch"; + spec = { + volumes.config.persistentVolumeClaim.claimName = "prowlarr"; + + containers.prowlarr = { + image = utils.mkNixNGImage "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 = 413; + fsGroupChangePolicy = "OnRootMismatch"; + }; }; }; }; }; - sonarr.spec = { - selector.matchLabels = { - app = "media"; - component = "sonarr"; - }; - - strategy = { - type = "RollingUpdate"; - - rollingUpdate = { - maxSurge = 0; - maxUnavailable = 1; - }; - }; - - template = { - metadata.labels = { + sonarr = lib.mkIf cfg.sonarr.enable { + spec = { + selector.matchLabels = { app = "media"; component = "sonarr"; }; - spec = { - containers.sonarr = { - image = utils.mkNixNGImage "sonarr"; - ports.web.containerPort = 8989; - imagePullPolicy = "IfNotPresent"; + strategy = { + type = "RollingUpdate"; - env = { - PUID.value = "1000"; - PGID.value = "1000"; - TZ.value = "Europe/Amsterdam"; + rollingUpdate = { + maxSurge = 0; + maxUnavailable = 1; + }; + }; + + template = { + metadata.labels = { + app = "media"; + component = "sonarr"; + }; + + spec = { + containers.sonarr = { + image = utils.mkNixNGImage "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"; + } + ]; }; - volumeMounts = [ - { - name = "config"; - mountPath = "/config"; - } - { - name = "media"; - mountPath = "/media"; - } - ]; - }; + volumes = { + config.persistentVolumeClaim.claimName = "sonarr"; + media.persistentVolumeClaim.claimName = "media"; + }; - volumes = { - config.persistentVolumeClaim.claimName = "sonarr"; - media.persistentVolumeClaim.claimName = "media"; - }; - - securityContext = { - fsGroup = 411; - fsGroupChangePolicy = "OnRootMismatch"; + securityContext = { + fsGroup = 411; + fsGroupChangePolicy = "OnRootMismatch"; + }; }; }; }; }; - bazarr.spec = { - selector.matchLabels = { - app = "media"; - component = "bazarr"; - }; - - strategy = { - type = "RollingUpdate"; - - rollingUpdate = { - maxSurge = 0; - maxUnavailable = 1; - }; - }; - - template = { - metadata.labels = { + bazarr = lib.mkIf cfg.bazarr.enable { + spec = { + selector.matchLabels = { app = "media"; component = "bazarr"; }; - spec = { - containers.bazarr = { - image = utils.mkNixNGImage "bazarr"; - ports.web.containerPort = 6767; - imagePullPolicy = "IfNotPresent"; + strategy = { + type = "RollingUpdate"; - env = { - PUID.value = "1000"; - PGID.value = "1000"; - TZ.value = "Europe/Amsterdam"; + rollingUpdate = { + maxSurge = 0; + maxUnavailable = 1; + }; + }; + + template = { + metadata.labels = { + app = "media"; + component = "bazarr"; + }; + + spec = { + containers.bazarr = { + image = utils.mkNixNGImage "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"; + } + ]; }; - volumeMounts = [ - { - name = "config"; - mountPath = "/config"; - } - { - name = "media"; - mountPath = "/media"; - } - ]; - }; + volumes = { + config.persistentVolumeClaim.claimName = "bazarr"; + media.persistentVolumeClaim.claimName = "media"; + }; - volumes = { - config.persistentVolumeClaim.claimName = "bazarr"; - media.persistentVolumeClaim.claimName = "media"; - }; - - securityContext = { - fsGroup = 412; - fsGroupChangePolicy = "OnRootMismatch"; + securityContext = { + fsGroup = 412; + fsGroupChangePolicy = "OnRootMismatch"; + }; }; }; }; @@ -426,120 +451,136 @@ }; 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"; + jellyfin = lib.mkIf cfg.jellyfin.enable { + spec = { + selector = { + app = "media"; + component = "jellyfin"; }; - web = { + ports.web = { port = 80; targetPort = "web"; }; }; }; - jellyseerr.spec = { - type = "LoadBalancer"; - loadBalancerIP = globals.jellyseerrIPv4; + deluge = lib.mkIf cfg.deluge.enable { + spec = { + type = "LoadBalancer"; + loadBalancerIP = globals.transmissionIPv4; - selector = { - app = "media"; - component = "jellyseerr"; - }; + selector = { + app = "media"; + component = "deluge"; + }; - ports.web = { - port = 80; - targetPort = "web"; + ports = { + bittorrent = { + port = 31780; + targetPort = "bittorrent"; + }; + + web = { + port = 80; + targetPort = "web"; + }; + }; }; }; - radarr.spec = { - type = "LoadBalancer"; - loadBalancerIP = globals.radarrIPv4; + jellyseerr = lib.mkIf cfg.jellyseerr.enable { + spec = { + type = "LoadBalancer"; + loadBalancerIP = globals.jellyseerrIPv4; - selector = { - app = "media"; - component = "radarr"; - }; + selector = { + app = "media"; + component = "jellyseerr"; + }; - ports.web = { - port = 80; - targetPort = "web"; + ports.web = { + port = 80; + targetPort = "web"; + }; }; }; - prowlarr.spec = { - type = "LoadBalancer"; - loadBalancerIP = globals.prowlarrIPv4; + radarr = lib.mkIf cfg.radarr.enable { + spec = { + type = "LoadBalancer"; + loadBalancerIP = globals.radarrIPv4; - selector = { - app = "media"; - component = "prowlarr"; - }; + selector = { + app = "media"; + component = "radarr"; + }; - ports.web = { - port = 80; - targetPort = "web"; + ports.web = { + port = 80; + targetPort = "web"; + }; }; }; - sonarr.spec = { - type = "LoadBalancer"; - loadBalancerIP = globals.sonarrIPv4; + prowlarr = lib.mkIf cfg.prowlarr.enable { + spec = { + type = "LoadBalancer"; + loadBalancerIP = globals.prowlarrIPv4; - selector = { - app = "media"; - component = "sonarr"; - }; + selector = { + app = "media"; + component = "prowlarr"; + }; - ports.web = { - port = 80; - targetPort = "web"; + ports.web = { + port = 80; + targetPort = "web"; + }; }; }; - bazarr.spec = { - type = "LoadBalancer"; - loadBalancerIP = globals.bazarrIPv4; + sonarr = lib.mkIf cfg.sonarr.enable { + spec = { + type = "LoadBalancer"; + loadBalancerIP = globals.sonarrIPv4; - selector = { - app = "media"; - component = "bazarr"; + selector = { + app = "media"; + component = "sonarr"; + }; + + ports.web = { + port = 80; + targetPort = "web"; + }; }; + }; - ports.web = { - port = 80; - targetPort = "web"; + bazarr = lib.mkIf cfg.bazarr.enable { + 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"; + jellyfin-cache = lib.mkIf cfg.jellyfin.enable { + spec = { + accessModes = ["ReadWriteOnce"]; + resources.requests.storage = "20Gi"; + }; }; media.spec = { @@ -553,7 +594,7 @@ lab = { ingresses = { - jellyfin = { + jellyfin = lib.mkIf cfg.jellyfin.enable { host = "media.kun.is"; service = { @@ -562,7 +603,7 @@ }; }; - jellyseerr = { + jellyseerr = lib.mkIf cfg.jellyseerr.enable { host = "jellyseerr.kun.is"; entrypoint = "localsecure"; @@ -574,69 +615,69 @@ }; tailscaleIngresses = { - tailscale-jellyseerr = { + tailscale-jellyseerr = lib.mkIf cfg.jellyseerr.enable { host = "jellyseerr"; service.name = "jellyseerr"; }; - tailscale-radarr = { + tailscale-radarr = lib.mkIf cfg.radarr.enable { host = "radarr"; service.name = "radarr"; }; - tailscale-sonarr = { + tailscale-sonarr = lib.mkIf cfg.sonarr.enable { host = "sonarr"; service.name = "sonarr"; }; - tailscale-bazarr = { + tailscale-bazarr = lib.mkIf cfg.bazarr.enable { host = "bazarr"; service.name = "bazarr"; }; - tailscale-prowlarr = { + tailscale-prowlarr = lib.mkIf cfg.prowlarr.enable { host = "prowlarr"; service.name = "prowlarr"; }; - tailscale-deluge = { + tailscale-deluge = lib.mkIf cfg.deluge.enable { host = "deluge"; service.name = "deluge"; }; }; longhorn.persistentVolumeClaim = { - jellyfin = { + jellyfin = lib.mkIf cfg.jellyfin.enable { volumeName = "jellyfin"; storage = "5Gi"; }; - deluge = { + deluge = lib.mkIf cfg.deluge.enable { volumeName = "deluge"; storage = "500Mi"; }; - jellyseerr = { + jellyseerr = lib.mkIf cfg.jellyseerr.enable { volumeName = "jellyseerr"; storage = "75Mi"; }; - radarr = { + radarr = lib.mkIf cfg.radarr.enable { volumeName = "radarr"; storage = "300Mi"; }; - prowlarr = { + prowlarr = lib.mkIf cfg.prowlarr.enable { volumeName = "prowlarr"; storage = "150Mi"; }; - sonarr = { + sonarr = lib.mkIf cfg.sonarr.enable { volumeName = "sonarr"; - storage = "150Mi"; + storage = "250Mi"; }; - bazarr = { + bazarr = lib.mkIf cfg.bazarr.enable { volumeName = "bazarr"; storage = "25Mi"; }; diff --git a/nixng-modules/sonarr.nix b/nixng-modules/sonarr.nix index 425033f..b78a4cc 100644 --- a/nixng-modules/sonarr.nix +++ b/nixng-modules/sonarr.nix @@ -24,6 +24,7 @@ in { enabled = true; user = lib.mkDefault "sonarr"; group = lib.mkDefault "sonarr"; + tmpfiles = with nglib.nottmpfiles.dsl; [(e "${cfg.dataDir}/logs" "-" cfgInit.user cfgInit.group "7d" _)]; script = pkgs.writeShellScript "sonarr-run" '' umask 0002 @@ -35,7 +36,7 @@ in { users.users.${cfgInit.user} = lib.mkIf (cfgInit.user == "sonarr") (nglib.mkDefaultRec { description = "sonarr"; - group = cfgInit.group; + inherit (cfgInit) group; createHome = false; home = "/var/empty"; useDefaultShell = true;