From 4826cb6a72655cb1bb3fe6378e252131751599e1 Mon Sep 17 00:00:00 2001 From: Chris Kruining Date: Mon, 8 Dec 2025 16:29:02 +0100 Subject: [PATCH] feat: implement more stuff with new servarr module --- modules/nixos/services/media/default.nix | 316 +++--------------- .../nixos/services/media/servarr/default.nix | 146 +++++--- systems/x86_64-linux/ulmo/default.nix | 10 +- 3 files changed, 152 insertions(+), 320 deletions(-) diff --git a/modules/nixos/services/media/default.nix b/modules/nixos/services/media/default.nix index c880580..d257aea 100644 --- a/modules/nixos/services/media/default.nix +++ b/modules/nixos/services/media/default.nix @@ -3,16 +3,12 @@ lib, namespace, config, - inputs, - system, ... }: let inherit (lib) mkIf mkEnableOption mkOption; inherit (lib.types) str; cfg = config.${namespace}.services.media; - - arr = ["radarr"]; in { options.${namespace}.services.media = { enable = mkEnableOption "Enable media services"; @@ -60,285 +56,75 @@ in { }; systemd.tmpfiles.rules = [ - "d '${cfg.path}/series' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/movies' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/music' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/qbittorrent' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/sabnzbd' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/reiverr/config' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/downloads/incomplete' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/downloads/done' 0700 ${cfg.user} ${cfg.group} - -" - "d /var/lib/radarrApplyTerraform 0755 ${cfg.user} ${cfg.group} -" + # "d '${cfg.path}/series' 0770 ${cfg.user} ${cfg.group} - -" + # "d '${cfg.path}/movies' 0770 ${cfg.user} ${cfg.group} - -" + # "d '${cfg.path}/music' 0770 ${cfg.user} ${cfg.group} - -" + "d '${cfg.path}/qbittorrent' 0770 ${cfg.user} ${cfg.group} - -" + "d '${cfg.path}/sabnzbd' 0770 ${cfg.user} ${cfg.group} - -" + "d '${cfg.path}/downloads/incomplete' 0770 ${cfg.user} ${cfg.group} - -" + "d '${cfg.path}/downloads/done' 0770 ${cfg.user} ${cfg.group} - -" ]; #========================================================================= # Services #========================================================================= - services = let - arr-services = - arr - |> lib.imap (i: service: { - name = service; - value = - { - enable = true; - openFirewall = true; - - environmentFiles = [ - config.sops.templates."${service}/config.env".path - ]; - - settings = { - auth.authenticationMethod = "External"; - - server = { - bindaddress = "0.0.0.0"; - port = 2000 + i; - }; - - postgres = { - host = "localhost"; - port = "5432"; - user = service; - maindb = service; - logdb = service; - }; - }; - } - // ( - if service != "prowlarr" - then { - user = cfg.user; - group = cfg.group; - } - else {} - ); - }) - |> lib.listToAttrs; - in - arr-services - // { - bazarr = { - enable = true; - openFirewall = true; - user = cfg.user; - group = cfg.group; - listenPort = 2005; - }; - - # port is harcoded in nixpkgs module - jellyfin = { - enable = true; - openFirewall = true; - user = cfg.user; - group = cfg.group; - }; - - flaresolverr = { - enable = true; - openFirewall = true; - port = 2007; - }; - - qbittorrent = { - enable = true; - openFirewall = true; - webuiPort = 2008; - - serverConfig = { - LegalNotice.Accepted = true; - - Prefecences.WebUI = { - Username = "admin"; - }; - }; - - user = cfg.user; - group = cfg.group; - }; - - # port is harcoded in nixpkgs module - sabnzbd = { - enable = true; - openFirewall = true; - configFile = "${cfg.path}/sabnzbd/config.ini"; - - user = cfg.user; - group = cfg.group; - }; - - postgresql = let - databases = arr |> lib.concatMap (s: [s "${s}-log"]); - in { - enable = true; - ensureDatabases = arr; - ensureUsers = - arr - |> lib.map (service: { - name = service; - ensureDBOwnership = true; - }); - }; - - caddy = { - enable = true; - virtualHosts = { - "jellyfin.kruining.eu".extraConfig = '' - reverse_proxy http://[::1]:8096 - ''; - }; - }; + services = { + bazarr = { + enable = true; + openFirewall = true; + user = cfg.user; + group = cfg.group; + listenPort = 2005; }; - systemd.services.radarrApplyTerraform = let - # this is a nix package, the generated json file to be exact - terraformConfiguration = inputs.terranix.lib.terranixConfiguration { - inherit system; - - modules = [ - ({ - config, - lib, - ... - }: { - config = { - variable = { - api_key = { - type = "string"; - description = "Radarr api key"; - }; - }; - - terraform.required_providers.radarr = { - source = "devopsarr/radarr"; - version = "2.2.0"; - }; - - provider.radarr = { - url = "http://127.0.0.1:2001"; - api_key = lib.tfRef "var.api_key"; - }; - - resource = { - radarr_root_folder.local = { - path = "/var/media/movies"; - }; - }; - }; - }) - ]; + flaresolverr = { + enable = true; + openFirewall = true; + port = 2007; }; - in { - description = "Radarr terraform apply"; - wantedBy = ["multi-user.target"]; - wants = ["radarr.service"]; + # port is harcoded in nixpkgs module + jellyfin = { + enable = true; + openFirewall = true; + user = cfg.user; + group = cfg.group; + }; - script = '' - #!/usr/bin/env bash + postgresql = { + enable = true; + }; - if [ "$(systemctl is-active radarr)" != "active" ]; then - echo "Radarr is not running" - exit 1 - fi - - # Sleep for a bit to give radarr the chance to start up - sleep 5s - - # Print the path to the source for easier debugging - echo "config location: ${terraformConfiguration}" - - # Copy infra code into workspace - cp -f ${terraformConfiguration} config.tf.json - - # Initialize OpenTofu - ${lib.getExe pkgs.opentofu} init - - # Run the infrastructure code - # ${lib.getExe pkgs.opentofu} plan -var-file='${config.sops.templates."radarr/config.tfvars".path}' - ${lib.getExe pkgs.opentofu} apply -auto-approve -var-file='${config.sops.templates."radarr/config.tfvars".path}' - ''; - - serviceConfig = { - Type = "oneshot"; - User = cfg.user; - Group = cfg.group; - - WorkingDirectory = "/var/lib/radarrApplyTerraform"; - - EnvironmentFile = [ - config.sops.templates."radarr/config.env".path - ]; + caddy = { + enable = true; + virtualHosts = { + "jellyfin.kruining.eu".extraConfig = '' + reverse_proxy http://[::1]:8096 + ''; + }; }; }; systemd.services.jellyfin.serviceConfig.killSignal = lib.mkForce "SIGKILL"; sops = { - secrets = let - arrSecrets = - arr - |> lib.map (service: { - name = "${service}/apikey"; - value = { - owner = cfg.user; - group = cfg.group; - restartUnits = ["${service}.service"]; - }; - }) - |> lib.listToAttrs; - in - arrSecrets - // { - # "qbittorrent/password" = {}; - "qbittorrent/password_hash" = {}; + secrets = { + # "qbittorrent/password" = {}; + "qbittorrent/password_hash" = {}; + }; + + templates = { + "qbittorrent/password.conf" = { + owner = cfg.user; + group = cfg.group; + restartUnits = ["qbittorrent.service"]; + path = "${config.services.qbittorrent.profileDir}/qBittorrent/config/password.conf"; + content = '' + [Preferences] + WebUI\Password_PBKDF2="${config.sops.placeholder."qbittorrent/password_hash"}" + ''; }; - - templates = let - apikeys = - arr - |> lib.map (service: { - name = "${service}/config.env"; - value = { - owner = cfg.user; - group = cfg.group; - restartUnits = ["${service}.service"]; - content = '' - ${lib.toUpper service}__AUTH__APIKEY="${config.sops.placeholder."${service}/apikey"}" - ''; - }; - }) - |> lib.listToAttrs; - - tfvars = - arr - |> lib.map (service: { - name = "${service}/config.tfvars"; - value = { - owner = cfg.user; - group = cfg.group; - restartUnits = ["${service}ApplyTerraform.service"]; - content = '' - api_key = "${config.sops.placeholder."${service}/apikey"}" - ''; - }; - }) - |> lib.listToAttrs; - - qbittorrent = { - "qbittorrent/password.conf" = { - owner = cfg.user; - group = cfg.group; - restartUnits = ["qbittorrent.service"]; - path = "${config.services.qbittorrent.profileDir}/qBittorrent/config/password.conf"; - content = '' - [Preferences] - WebUI\Password_PBKDF2="${config.sops.placeholder."qbittorrent/password_hash"}" - ''; - }; - }; - in - apikeys // tfvars // qbittorrent; + }; }; }; } diff --git a/modules/nixos/services/media/servarr/default.nix b/modules/nixos/services/media/servarr/default.nix index 733fe99..373e09b 100644 --- a/modules/nixos/services/media/servarr/default.nix +++ b/modules/nixos/services/media/servarr/default.nix @@ -41,36 +41,68 @@ in { port, ... }: (mkIf enable { - "${service}" = { - enable = true; - openFirewall = true; + "${service}" = + { + enable = true; + openFirewall = true; - environmentFiles = [ - config.sops.templates."${service}/config.env".path - ]; + environmentFiles = [ + config.sops.templates."${service}/config.env".path + ]; - settings = { - auth.authenticationMethod = "External"; + settings = { + auth.authenticationMethod = "External"; - server = { - bindaddress = "0.0.0.0"; - port = port; + server = { + bindaddress = "0.0.0.0"; + port = port; + }; + + postgres = { + host = "localhost"; + port = "5432"; + user = service; + maindb = service; + logdb = service; + }; }; - - postgres = { - host = "localhost"; - port = "5432"; - user = service; - maindb = service; - logdb = service; - }; - }; - }; + } + // (lib.optionalAttrs (service != "prowlarr") { + user = service; + group = "media"; + }); })) - |> lib.mergeAttrsList + |> lib.mkMerge |> (set: set // { + qbittorrent = { + enable = true; + openFirewall = true; + webuiPort = 2008; + + serverConfig = { + LegalNotice.Accepted = true; + + Prefecences.WebUI = { + Username = "admin"; + }; + }; + + user = "qbittorrent"; + group = "media"; + }; + + # port is harcoded in nixpkgs module + sabnzbd = { + enable = true; + openFirewall = true; + configFile = "${cfg.path}/sabnzbd/config.ini"; + + user = "sabnzbd"; + group = "media"; + }; + postgresql = { ensureDatabases = cfg |> lib.attrNames; ensureUsers = @@ -83,7 +115,7 @@ in { }; }); - systemd = + systemd.services = cfg |> lib.mapAttrsToList (service: { enable, @@ -92,11 +124,7 @@ in { rootFolders, ... }: (mkIf enable { - tmpfiles.rules = [ - "d /var/lib/${service}ApplyTerraform 0755 ${service} ${service} -" - ]; - - services."${service}ApplyTerraform" = let + "${service}ApplyTerraform" = let terraformConfiguration = inputs.terranix.lib.terranixConfiguration { inherit system; @@ -116,7 +144,17 @@ in { terraform.required_providers.${service} = { source = "devopsarr/${service}"; - version = "2.2.0"; + version = + { + radarr = "2.3.3"; + sonarr = "3.4.0"; + prowlarr = "3.1.0"; + lidarr = "1.13.0"; + readarr = "2.1.0"; + whisparr = "1.2.0"; + }.${ + service + }; }; provider.${service} = { @@ -125,10 +163,11 @@ in { }; resource = { - "${service}_root_folder" = + "${service}_root_folder" = mkIf (lib.elem service ["radarr" "sonarr" "whisparr"]) ( rootFolders |> lib.imap (i: f: lib.nameValuePair "local${toString i}" {path = f;}) - |> lib.listToAttrs; + |> lib.listToAttrs + ); }; }; }) @@ -140,9 +179,16 @@ in { wantedBy = ["multi-user.target"]; wants = ["${service}.service"]; - script = '' - #!/usr/bin/env bash + preStart = '' + install -d -m 0770 -o ${service} -g media /var/lib/${service}ApplyTerraform + ${ + rootFolders + |> lib.map (folder: "install -d -m 0770 -o media -g media ${folder}") + |> lib.join "\n" + } + ''; + script = '' # Sleep for a bit to give the service a chance to start up sleep 5s @@ -158,7 +204,7 @@ in { cp -f ${terraformConfiguration} config.tf.json # Initialize OpenTofu - ${lib.getExe pkgs.opentofu} init + ${lib.getExe pkgs.opentofu} init -upgrade # Run the infrastructure code ${lib.getExe pkgs.opentofu} \ @@ -173,7 +219,7 @@ in { serviceConfig = { Type = "oneshot"; User = service; - Group = service; + Group = "media"; WorkingDirectory = "/var/lib/${service}ApplyTerraform"; @@ -183,28 +229,33 @@ in { }; }; })) - |> lib.mergeAttrsList; + |> lib.mkMerge; - users.users = + users = cfg |> lib.mapAttrsToList (service: {enable, ...}: (mkIf enable { - "${service}".extraGroups = ["media"]; + users.${service} = { + isSystemUser = true; + group = lib.mkDefault service; + extraGroups = ["media"]; + }; + groups.${service} = {}; })) - |> lib.mergeAttrsList; + |> lib.mkMerge; sops = cfg |> lib.mapAttrsToList (service: {enable, ...}: (mkIf enable { secrets."${service}/apikey" = { owner = service; - group = service; + group = "media"; restartUnits = ["${service}.service"]; }; templates = { "${service}/config.env" = { owner = service; - group = service; + group = "media"; restartUnits = ["${service}.service"]; content = '' ${lib.toUpper service}__AUTH__APIKEY="${config.sops.placeholder."${service}/apikey"}" @@ -213,7 +264,7 @@ in { "${service}/config.tfvars" = { owner = service; - group = service; + group = "media"; restartUnits = ["${service}.service"]; content = '' api_key = "${config.sops.placeholder."${service}/apikey"}" @@ -221,15 +272,6 @@ in { }; }; })) - |> lib.mergeAttrsList; + |> lib.mkMerge; }; - - # cfg - # |> lib.mapAttrsToList (service: { enable, debug, port, rootFolders, ... }: (mkIf enable { - - # # sops = { - # # }; - # })) - # |> lib.mergeAttrsList - # ; } diff --git a/systems/x86_64-linux/ulmo/default.nix b/systems/x86_64-linux/ulmo/default.nix index 3638dbc..9d12de8 100644 --- a/systems/x86_64-linux/ulmo/default.nix +++ b/systems/x86_64-linux/ulmo/default.nix @@ -165,9 +165,13 @@ media.mydia.enable = true; media.nfs.enable = true; media.servarr = { - # radarr = { - # port = 2001; - # }; + radarr = { + enable = true; + port = 2001; + rootFolders = [ + "/var/media/movies" + ]; + }; sonarr = { enable = true;