{ config, containerCfg, pkgs, lib, builder, name, ... }: let serverCfg = config.syscfg.server; defaultModules = ["prowlarr" "sonarr" "radarr" "lidarr" "flaresolverr" ]; mkServarrImage = appName: appPkg: binaryPath: pkgs.dockerTools.streamLayeredImage { name = appPkg.name; tag = appPkg.version; contents = with pkgs; [ cacert openssl ]; config = { Cmd = [ "${appPkg}/${binaryPath}" "-nobrowser" "-data=/config" ]; Env = [ "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1" "HOME=/tmp" ]; }; }; images = { prowlarr = mkServarrImage "prowlarr" pkgs.prowlarr "bin/Prowlarr"; radarr = mkServarrImage "radarr" pkgs.radarr "bin/Radarr"; sonarr = mkServarrImage "sonarr" pkgs.sonarr "bin/Sonarr"; bazarr = mkServarrImage "bazarr" pkgs.bazarr "bin/bazarr"; lidarr = mkServarrImage "lidarr" pkgs.lidarr "bin/Lidarr"; readarr = mkServarrImage "readarr" pkgs.readarr "bin/Readarr"; }; sharedVolumes = [ "${serverCfg.dataPath}/media:/media" # Fast hardlinking requires a single shared root "${serverCfg.configPath}/servarr:/config-root" ]; in assert containerCfg.subpath == null || throw "Error: Servarr does not support subpath."; { sops = true; # db = [ "prowlarr" "sonarr" "radarr" ]; -> one db for each paths = [ { path = "${serverCfg.dataPath}/media/"; mode = "0755"; } { path = "${serverCfg.configPath}/servarr/prowlarr"; mode = "0755"; } { path = "${serverCfg.configPath}/servarr/radarr"; mode = "0755"; } { path = "${serverCfg.configPath}/servarr/sonarr"; mode = "0755"; } ]; containers = { }// lib.optionalAttrs (builtins.elem "prowlarr" (containerCfg.extra.modules or defaultModules)) { prowlarr = builder.mkContainer { subdomain = containerCfg.subdomain; subpath = "prowlarr"; imageStream = images.prowlarr; port = 8989; secret = name; extraEnv = { "PROWLARR__APP__INSTANCENAME" = "Prowlarr"; "PROWLARR__AUTH__METHOD" = "External"; "PROWLARR__SERVER__PORT" = "8989"; "PROWLARR__SERVER__URLBASE" = "/prowlarr"; "PROWLARR__LOG__ANALYTICSENABLED" = "False"; }; extraOptions = [ "--user=0:0" "--tmpfs=/tmp:rw,noexec,nosuid,size=512m" "--passwd-entry=root:x:0:0:root:/root:/bin/sh" ]; overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/prowlarr:/config" ]; }; }// lib.optionalAttrs (builtins.elem "radarr" (containerCfg.extra.modules or defaultModules)) { radarr = builder.mkContainer { subdomain = containerCfg.subdomain; subpath = "radarr"; imageStream = images.radarr; port = 8989; secret = name; extraEnv = { "RADARR__APP__INSTANCENAME" = "Radarr"; "RADARR__AUTH__METHOD" = "External"; "RADARR__SERVER__PORT" = "8989"; "RADARR__SERVER__URLBASE" = "/radarr"; "RADARR__LOG__ANALYTICSENABLED" = "False"; }; extraOptions = [ "--user=0:0" "--tmpfs=/tmp:rw,noexec,nosuid,size=512m" "--passwd-entry=root:x:0:0:root:/root:/bin/sh" ]; overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/radarr:/config" ]; }; }// lib.optionalAttrs (builtins.elem "sonarr" (containerCfg.extra.modules or defaultModules)) { sonarr = builder.mkContainer { subdomain = containerCfg.subdomain; subpath = "sonarr"; imageStream = images.sonarr; port = 8989; secret = name; extraEnv = { "SONARR__APP__INSTANCENAME" = "Sonarr"; "SONARR__AUTH__METHOD" = "External"; "SONARR__SERVER__PORT" = "8989"; "SONARR__SERVER__URLBASE" = "/sonarr"; "SONARR__LOG__ANALYTICSENABLED" = "False"; }; extraOptions = [ "--user=0:0" "--tmpfs=/tmp:rw,noexec,nosuid,size=512m" "--passwd-entry=root:x:0:0:root:/root:/bin/sh" ]; overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/sonarr:/config" ]; }; }// lib.optionalAttrs (builtins.elem "readarr" (containerCfg.extra.modules or defaultModules)) { readarr = throw "Not Implemented"; }// lib.optionalAttrs (builtins.elem "mylarr" (containerCfg.extra.modules or defaultModules)) { mylarr = throw "Not Implemented"; }// lib.optionalAttrs (builtins.elem "bazarr" (containerCfg.extra.modules or defaultModules)) { bazarr = throw "Not Implemented"; }// lib.optionalAttrs (builtins.elem "seerr" (containerCfg.extra.modules or defaultModules)) { seerr = throw "Not Implemented"; }// lib.optionalAttrs (builtins.elem "flaresolverr" (containerCfg.extra.modules or defaultModules)) { flaresolverr = builder.mkContainer { image = "ghcr.io/flaresolverr/flaresolverr:latest"; port = 8191; extraEnv = { "CAPTCHA_SOLVER" = "none"; }; }; }; setup = { trigger = "prowlarr"; # Triggers atomic environment verification on main controller envFile = config.sops.secrets."SERVARR".path; script = pkgs.writeShellScript "setup-servarr" '' ${lib.optionalString (lib.all (x: builtins.elem x (containerCfg.extra.modules or defaultModules)) [ "prowlarr" "flaresolverr" ]) '' PROWL_PROXY=$(${pkgs.curl}/bin/curl -s -X GET 'https://${containerCfg.subdomain}.${serverCfg.domain}/prowlarr/api/v1/indexerProxy' \ -H "X-Api-Key: $PROWLARR__AUTH__APIKEY" -H 'X-Prowlarr-Client: true' \ -H 'Accept: application/json' -H 'Content-Type: application/json') ${pkgs.curl}/bin/curl -s -X POST 'https://${containerCfg.subdomain}.${serverCfg.domain}/prowlarr/api/v1/indexerProxy' \ -H "X-Api-Key: $PROWLARR__AUTH__APIKEY" -H 'X-Prowlarr-Client: true' \ -H 'Accept: application/json' -H 'Content-Type: application/json' \ -d '{"onHealthIssue":false,"supportsOnHealthIssue":false,"includeHealthWarnings":false,"name":"FlareSolverr","fields":[{"name":"host","value":"http://servarr-flaresolverr:8191/"},{"name":"requestTimeout","value":60}],"implementationName":"FlareSolverr","implementation":"FlareSolverr","configContract":"FlareSolverrSettings","infoLink":"https://wiki.servarr.com/prowlarr/supported#flaresolverr","tags":[]}' ''} PROWL_APPS=$(${pkgs.curl}/bin/curl -s -X GET 'https://${containerCfg.subdomain}.${serverCfg.domain}/prowlarr/api/v1/applications' \ -H "X-Api-Key: $PROWLARR__AUTH__APIKEY" -H 'X-Prowlarr-Client: true' \ -H 'Accept: application/json' -H 'Content-Type: application/json') ${lib.optionalString (lib.all (x: builtins.elem x (containerCfg.extra.modules or defaultModules)) [ "prowlarr" "sonarr" ]) '' ${pkgs.curl}/bin/curl -s -X POST 'https://${containerCfg.subdomain}.${serverCfg.domain}/prowlarr/api/v1/applications' \ -H "X-Api-Key: $PROWLARR__AUTH__APIKEY" -H 'X-Prowlarr-Client: true' \ -H 'Accept: application/json' -H 'Content-Type: application/json' \ -d '{"syncLevel":"fullSync","enable":true,"fields":[{"name":"prowlarrUrl","value":"http://servarr-prowlarr:8989"},{"name":"baseUrl","value":"https://servarr-sonarr:8989"},{"name":"apiKey","value":"'"$SONARR__AUTH__APIKEY"'"},{"name":"syncCategories","value":[5000,5010,5020,5030,5040,5045,5050,5090]},{"name":"animeSyncCategories","value":[5070]},{"name":"syncAnimeStandardFormatSearch","value":true},{"name":"syncRejectBlocklistedTorrentHashesWhileGrabbing","value":false}],"implementationName":"Sonarr","implementation":"Sonarr","configContract":"SonarrSettings","infoLink":"https://wiki.servarr.com/prowlarr/supported#sonarr","tags":[],"name":"Sonarr"}' ''} ${lib.optionalString (lib.all (x: builtins.elem x (containerCfg.extra.modules or defaultModules)) [ "prowlarr" "lidarr" ]) '' ${pkgs.curl}/bin/curl -s -X POST 'https://${containerCfg.subdomain}.${serverCfg.domain}/prowlarr/api/v1/applications' \ -H "X-Api-Key: $PROWLARR__AUTH__APIKEY" -H 'X-Prowlarr-Client: true' \ -H 'Accept: application/json' -H 'Content-Type: application/json' \ -d '{"syncLevel":"fullSync","enable":true,"fields":[{"name":"prowlarrUrl","value":"http://servarr-prowlarr:8989"},{"name":"baseUrl","value":"https://servarr-lidarr:8989"},{"name":"apiKey","value":"'"$LIDARR__AUTH__APIKEY"'"},{"name":"syncCategories","value":[5000,5010,5020,5030,5040,5045,5050,5090]},{"name":"animeSyncCategories","value":[5070]},{"name":"syncAnimeStandardFormatSearch","value":true},{"name":"syncRejectBlocklistedTorrentHashesWhileGrabbing","value":false}],"implementationName":"Lidarr","implementation":"Lidarr","configContract":"LidarrSettings","infoLink":"https://wiki.servarr.com/prowlarr/supported#lidarr","tags":[],"name":"Lidarr"}' ''} ${lib.optionalString (lib.all (x: builtins.elem x (containerCfg.extra.modules or defaultModules)) [ "prowlarr" "radarr" ]) '' ${pkgs.curl}/bin/curl -s -X POST 'https://${containerCfg.subdomain}.${serverCfg.domain}/prowlarr/api/v1/applications' \ -H "X-Api-Key: $PROWLARR__AUTH__APIKEY" -H 'X-Prowlarr-Client: true' \ -H 'Accept: application/json' -H 'Content-Type: application/json' \ -d '{"syncLevel":"fullSync","enable":true,"fields":[{"name":"prowlarrUrl","value":"http://servarr-prowlarr:8989"},{"name":"baseUrl","value":"https://servarr-radarr:8989"},{"name":"apiKey","value":"'"$RADARR__AUTH__APIKEY"'"},{"name":"syncCategories","value":[5000,5010,5020,5030,5040,5045,5050,5090]},{"name":"animeSyncCategories","value":[5070]},{"name":"syncAnimeStandardFormatSearch","value":true},{"name":"syncRejectBlocklistedTorrentHashesWhileGrabbing","value":false}],"implementationName":"Radarr","implementation":"Radarr","configContract":"RadarrSettings","infoLink":"https://wiki.servarr.com/prowlarr/supported#lidarr","tags":[],"name":"Radarr"}' ''} ${lib.optionalString (lib.all (x: builtins.elem x (containerCfg.extra.modules or defaultModules)) [ "prowlarr" "flaresolverr" ]) '' ''} ${lib.optionalString ((lib.all (x: builtins.elem x (containerCfg.extra.modules or defaultModules)) [ "prowlarr" ]) && serverCfg.containers?transmission ) '' PROWL_DL=$(${pkgs.curl}/bin/curl -s -X GET 'https://${containerCfg.subdomain}.${serverCfg.domain}/prowlarr/api/v1/downloadclient' \ -H "X-Api-Key: $PROWLARR__AUTH__APIKEY" -H 'X-Prowlarr-Client: true' \ -H 'Accept: application/json' -H 'Content-Type: application/json') ${pkgs.curl}/bin/curl -s -X POST 'https://${containerCfg.subdomain}.${serverCfg.domain}/prowlarr/api/v1/downloadclient' \ -H "X-Api-Key: $PROWLARR__AUTH__APIKEY" -H 'X-Prowlarr-Client: true' \ -H 'Accept: application/json' -H 'Content-Type: application/json' \ -d '{"enable":true,"protocol":"torrent","priority":1,"categories":[],"supportsCategories":true,"name":"Transmission","fields":[{"name":"host","value":"transmission-server"},{"name":"port","value":9091},{"name":"useSsl","value":false},{"name":"urlBase","value":"/transmission/"},{"name":"username"},{"name":"password"},{"name":"category"},{"name":"directory"},{"name":"priority","value":0},{"name":"addPaused","value":false}],"implementationName":"Transmission","implementation":"Transmission","configContract":"TransmissionSettings","infoLink":"https://wiki.servarr.com/prowlarr/supported#transmission","tags":[]}' ''} ${lib.optionalString (lib.all (x: builtins.elem x (containerCfg.extra.modules or defaultModules)) [ "prowlarr" ]) '' PROWL_IDX=$(${pkgs.curl}/bin/curl -s -X GET 'https://${containerCfg.subdomain}.${serverCfg.domain}/prowlarr/api/v1/indexer' \ -H "X-Api-Key: $PROWLARR__AUTH__APIKEY" -H 'X-Prowlarr-Client: true' \ -H 'Accept: application/json' -H 'Content-Type: application/json') echo $PROWL_IDX #... For extra.indexer -> ... ''} ''; }; }