dotfiles/modules/nixos/services/arr.nix

286 lines
7.6 KiB
Nix
Raw Normal View History

2024-04-20 13:42:06 +00:00
{
config,
pkgs,
lib,
...
}:
2023-07-25 02:55:30 +00:00
let
2024-01-10 04:11:11 +00:00
# This config specifies ports for Prometheus to scrape information
2023-07-25 02:55:30 +00:00
arrConfig = {
radarr = {
exportarrPort = "9707";
url = "localhost:7878";
apiKey = config.secrets.radarrApiKey.dest;
};
2024-10-12 15:18:54 +00:00
readarr = {
exportarrPort = "9711";
url = "localhost:8787";
apiKey = config.secrets.readarrApiKey.dest;
};
2023-07-25 02:55:30 +00:00
sonarr = {
exportarrPort = "9708";
url = "localhost:8989";
apiKey = config.secrets.sonarrApiKey.dest;
};
prowlarr = {
exportarrPort = "9709";
url = "localhost:9696";
apiKey = config.secrets.prowlarrApiKey.dest;
};
sabnzbd = {
exportarrPort = "9710";
url = "localhost:8085";
apiKey = config.secrets.sabnzbdApiKey.dest;
};
};
2024-04-20 13:42:06 +00:00
in
{
2023-07-25 02:55:30 +00:00
2024-04-20 13:42:06 +00:00
options = {
arrs.enable = lib.mkEnableOption "Arr services";
};
2023-03-12 13:24:16 +00:00
config = lib.mkIf config.arrs.enable {
2023-03-12 13:24:16 +00:00
# Broken on 2024-12-07
# https://discourse.nixos.org/t/solved-sonarr-is-broken-in-24-11-unstable-aka-how-the-hell-do-i-use-nixpkgs-config-permittedinsecurepackages/
insecurePackages = [
"aspnetcore-runtime-wrapped-6.0.36"
"aspnetcore-runtime-6.0.36"
"dotnet-sdk-wrapped-6.0.428"
"dotnet-sdk-6.0.428"
];
2023-06-06 00:56:52 +00:00
services = {
bazarr = {
enable = true;
group = "shared";
2023-06-06 00:56:52 +00:00
};
jellyseerr.enable = true;
prowlarr.enable = true;
sabnzbd = {
enable = true;
group = "shared";
2024-01-10 04:11:11 +00:00
# The config file must be editable within the application
# It contains server configs and credentials
2023-06-06 00:56:52 +00:00
configFile = "/data/downloads/sabnzbd/sabnzbd.ini";
};
sonarr = {
enable = true;
group = "shared";
2023-06-06 00:56:52 +00:00
};
radarr = {
enable = true;
group = "shared";
2023-06-06 00:56:52 +00:00
};
2024-10-12 15:18:54 +00:00
readarr = {
enable = true;
group = "shared";
2024-10-12 15:18:54 +00:00
};
2023-06-06 00:56:52 +00:00
};
2023-03-12 13:24:16 +00:00
# Allows shared group to read/write the sabnzbd directory
2023-06-04 14:19:56 +00:00
users.users.sabnzbd.homeMode = "0770";
2023-06-06 00:56:52 +00:00
2024-01-10 04:11:11 +00:00
unfreePackages = [ "unrar" ]; # Required as a dependency for sabnzbd
2023-03-12 13:24:16 +00:00
# Requires updating the base_url config value in each service
# If you try to rewrite the URL, the service won't redirect properly
caddy.routes = [
{
2024-01-10 04:11:11 +00:00
# Group means that routes with the same name are mutually exclusive,
# so they are split between the appropriate services.
2023-03-12 13:24:16 +00:00
group = "download";
2024-04-20 13:42:06 +00:00
match = [
{
host = [ config.hostnames.download ];
path = [ "/sonarr*" ];
}
];
handle = [
{
handler = "reverse_proxy";
# We're able to reference the url and port of the service dynamically
2024-04-20 13:42:06 +00:00
upstreams = [ { dial = arrConfig.sonarr.url; } ];
}
];
2023-03-12 13:24:16 +00:00
}
{
group = "download";
2024-04-20 13:42:06 +00:00
match = [
{
host = [ config.hostnames.download ];
path = [ "/radarr*" ];
}
];
handle = [
{
handler = "reverse_proxy";
upstreams = [ { dial = arrConfig.radarr.url; } ];
}
];
2023-03-12 13:24:16 +00:00
}
2024-10-12 15:18:54 +00:00
{
group = "download";
match = [
{
host = [ config.hostnames.download ];
path = [ "/readarr*" ];
}
];
handle = [
{
handler = "reverse_proxy";
upstreams = [ { dial = arrConfig.readarr.url; } ];
}
];
}
2023-03-12 13:24:16 +00:00
{
group = "download";
2024-04-20 13:42:06 +00:00
match = [
{
host = [ config.hostnames.download ];
path = [ "/prowlarr*" ];
}
];
handle = [
{
handler = "reverse_proxy";
# Prowlarr doesn't offer a dynamic config, so we have to hardcode it
upstreams = [ { dial = "localhost:9696"; } ];
}
];
2023-03-12 13:24:16 +00:00
}
{
group = "download";
2024-04-20 13:42:06 +00:00
match = [
{
host = [ config.hostnames.download ];
path = [ "/bazarr*" ];
}
];
handle = [
{
handler = "reverse_proxy";
upstreams = [
{
# Bazarr only dynamically sets the port, not the host
dial = "localhost:${builtins.toString config.services.bazarr.listenPort}";
}
];
}
];
2023-03-12 13:24:16 +00:00
}
2023-06-03 23:00:18 +00:00
{
group = "download";
2024-04-20 13:42:06 +00:00
match = [
{
host = [ config.hostnames.download ];
path = [ "/sabnzbd*" ];
}
];
handle = [
{
handler = "reverse_proxy";
upstreams = [ { dial = arrConfig.sabnzbd.url; } ];
}
];
2023-06-03 23:00:18 +00:00
}
2023-06-04 01:10:23 +00:00
{
group = "download";
2024-04-20 13:42:06 +00:00
match = [ { host = [ config.hostnames.download ]; } ];
handle = [
{
handler = "reverse_proxy";
upstreams = [ { dial = "localhost:${builtins.toString config.services.jellyseerr.port}"; } ];
}
];
2023-06-04 01:10:23 +00:00
}
2023-03-12 13:24:16 +00:00
];
# Configure Cloudflare DNS to point to this machine
services.cloudflare-dyndns.domains = [ config.hostnames.download ];
2023-07-25 02:55:30 +00:00
# Enable Prometheus exporters
systemd.services = lib.mapAttrs' (name: attrs: {
name = "prometheus-${name}-exporter";
value = {
description = "Export Prometheus metrics for ${name}";
after = [ "network.target" ];
wantedBy = [ "${name}.service" ];
serviceConfig = {
Type = "simple";
DynamicUser = true;
2024-04-20 13:42:06 +00:00
ExecStart =
let
# Sabnzbd doesn't accept the URI path, unlike the others
url = if name != "sabnzbd" then "http://${attrs.url}/${name}" else "http://${attrs.url}";
in
2024-01-10 04:11:11 +00:00
# Exportarr is trained to pull from the arr services
2024-04-20 13:42:06 +00:00
''
${pkgs.exportarr}/bin/exportarr ${name} \
--url ${url} \
--port ${attrs.exportarrPort}'';
EnvironmentFile = lib.mkIf (builtins.hasAttr "apiKey" attrs) attrs.apiKey;
2023-07-25 02:55:30 +00:00
Restart = "on-failure";
ProtectHome = true;
ProtectSystem = "strict";
PrivateTmp = true;
PrivateDevices = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
NoNewPrivileges = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
RemoveIPC = true;
PrivateMounts = true;
};
};
}) arrConfig;
# Secrets for Prometheus exporters
secrets.radarrApiKey = {
source = ../../../private/radarr-api-key.age;
dest = "/var/private/radarr-api";
prefix = "API_KEY=";
};
2024-10-12 15:18:54 +00:00
secrets.readarrApiKey = {
source = ../../../private/radarr-api-key.age;
dest = "/var/private/readarr-api";
prefix = "API_KEY=";
};
2023-07-25 02:55:30 +00:00
secrets.sonarrApiKey = {
source = ../../../private/sonarr-api-key.age;
dest = "/var/private/sonarr-api";
prefix = "API_KEY=";
};
secrets.prowlarrApiKey = {
source = ../../../private/prowlarr-api-key.age;
dest = "/var/private/prowlarr-api";
prefix = "API_KEY=";
};
secrets.sabnzbdApiKey = {
source = ../../../private/sabnzbd-api-key.age;
dest = "/var/private/sabnzbd-api";
prefix = "API_KEY=";
};
2024-01-10 04:11:11 +00:00
# Prometheus scrape targets (expose Exportarr to Prometheus)
2024-04-20 13:42:06 +00:00
prometheus.scrapeTargets = map (
key:
2023-07-25 02:55:30 +00:00
"127.0.0.1:${
2024-04-20 13:42:06 +00:00
lib.attrsets.getAttrFromPath [
key
"exportarrPort"
] arrConfig
}"
) (builtins.attrNames arrConfig);
2023-03-12 13:24:16 +00:00
};
}