{ config, lib, pkgs, ... }: let cfg = config.homelab.apps.arr; networkName = "arrStack"; appNames = [ "bazarr" "lidarr" "prowlarr" "qbittorrent" "radarr" "sonarr" ]; inUse = builtins.any (app: cfg.${app}.enable) appNames; PGID = toString config.users.groups.media.gid; UMASK = "002"; in { options.homelab.apps.arr = { enable = lib.mkEnableOption "Arr Stack using Docker"; bazarr.enable = lib.mkEnableOption "Bazarr using Docker"; lidarr.enable = lib.mkEnableOption "Lidarr using Docker"; prowlarr.enable = lib.mkEnableOption "Prowlarr using Docker"; qbittorrent.enable = lib.mkEnableOption "qBittorrent using Docker"; radarr.enable = lib.mkEnableOption "Radarr using Docker"; sonarr.enable = lib.mkEnableOption "Sonarr using Docker"; }; config = { homelab = { users.media.enable = lib.mkIf inUse true; # "Master switch": Enable all apps. apps.arr = { bazarr.enable = lib.mkIf cfg.enable true; lidarr.enable = lib.mkIf cfg.enable true; prowlarr.enable = lib.mkIf cfg.enable true; qbittorrent.enable = lib.mkIf cfg.enable true; radarr.enable = lib.mkIf cfg.enable true; sonarr.enable = lib.mkIf cfg.enable true; }; }; fileSystems = lib.mkIf inUse { "/srv/video" = { device = "192.168.0.11:/mnt/SMALL/MEDIA/VIDEO"; fsType = "nfs"; options = [ "rw" "nfsvers=4.2" "async" "soft" "timeo=100" "retry=50" "actimeo=1800" "lookupcache=all" "nosuid" "tcp" ]; }; "/srv/qbittorrent" = { device = "192.168.0.11:/mnt/SMALL/CONFIG/QBITTORRENT/qBittorrent"; fsType = "nfs"; options = [ "rw" "nfsvers=4.2" "async" "soft" "nosuid" "tcp" ]; }; }; # Make sure the Docker network exists. systemd.services."docker-${networkName}-create-network" = lib.mkIf inUse { description = "Create Docker network for ${networkName}"; requiredBy = [ "docker-bazarr.service" "docker-lidarr.service" "docker-prowlarr.service" "docker-qbittorrent.service" "docker-radarr.service" "docker-sonarr.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' if ! ${pkgs.docker}/bin/docker network ls | grep -q ${networkName}; then ${pkgs.docker}/bin/docker network create ${networkName} fi ''; }; # Create a user for each app. users.users = { bazarr = lib.mkIf cfg.bazarr.enable { uid = lib.mkForce 3003; isSystemUser = true; group = config.users.groups.media.name; home = "/var/empty"; shell = null; }; lidarr = lib.mkIf cfg.lidarr.enable { uid = lib.mkForce 3002; isSystemUser = true; group = config.users.groups.media.name; home = "/var/empty"; shell = null; }; prowlarr = lib.mkIf cfg.prowlarr.enable { uid = lib.mkForce 3004; isSystemUser = true; group = config.users.groups.media.name; home = "/var/empty"; shell = null; }; qbittorrent = lib.mkIf cfg.qbittorrent.enable { uid = lib.mkForce 3005; isSystemUser = true; group = config.users.groups.media.name; home = "/var/empty"; shell = null; }; radarr = lib.mkIf cfg.radarr.enable { uid = lib.mkForce 3006; isSystemUser = true; group = config.users.groups.media.name; home = "/var/empty"; shell = null; }; sonarr = lib.mkIf cfg.sonarr.enable { uid = lib.mkForce 3007; isSystemUser = true; group = config.users.groups.media.name; home = "/var/empty"; shell = null; }; }; virtualisation.oci-containers.containers = { bazarr = lib.mkIf cfg.bazarr.enable { hostname = "bazarr"; image = "ghcr.io/hotio/bazarr:release-1.4.4"; autoStart = true; ports = [ "6767:6767/tcp" "6767:6767/udp" ]; extraOptions = [ "--network=${networkName}" "--mount" ''type=volume,source=bazarr-backup,target=/backup,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/mnt/BIG/BACKUP/BAZARR,"volume-opt=o=addr=192.168.0.11,rw,nfsvers=4.2,async,nosuid"'' ]; environment = { PUID = toString config.users.users.bazarr.uid; inherit PGID UMASK; TZ = config.time.timeZone; WEBUI_PORTS = "6767/tcp,6767/udp"; }; volumes = [ "bazarr-config:/config" "/srv/video:/data" ]; }; lidarr = lib.mkIf cfg.lidarr.enable { hostname = "lidarr"; image = "ghcr.io/hotio/lidarr:release-2.5.3.4341"; autoStart = true; ports = [ "8686:8686/tcp" ]; extraOptions = [ "--network=${networkName}" "--mount" ''type=volume,source=lidarr-backup,target=/backup,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/mnt/BIG/BACKUP/LIDARR,"volume-opt=o=addr=192.168.0.11,rw,nfsvers=4.2,async,nosuid"'' ]; environment = { PUID = toString config.users.users.lidarr.uid; inherit PGID UMASK; TZ = config.time.timeZone; }; volumes = [ "lidarr-config:/config" # TODO "data:/data" ]; }; prowlarr = lib.mkIf cfg.prowlarr.enable { hostname = "prowlarr"; image = "ghcr.io/hotio/prowlarr:release-1.23.1.4708"; autoStart = true; ports = [ "9696:9696/tcp" ]; extraOptions = [ "--network=${networkName}" ]; environment = { PUID = toString config.users.users.prowlarr.uid; inherit PGID UMASK; TZ = config.time.timeZone; }; volumes = [ # TODO "config:/config" ]; }; qbittorrent = lib.mkIf cfg.qbittorrent.enable { hostname = "qbittorrent"; image = "ghcr.io/hotio/qbittorrent:release-4.6.7"; autoStart = true; ports = [ "10095:10095/udp" "10095:10095/tcp" ]; extraOptions = [ "--network=${networkName}" "--mount" ''type=volume,source=torrents,target=/data,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/mnt/SMALL/MEDIA/TORRENT,"volume-opt=o=addr=192.168.0.11,rw,nfsvers=4.2,async,nosuid"'' ]; environment = { PUID = toString config.users.users.qbittorrent.uid; inherit PGID UMASK; TZ = config.time.timeZone; WEBUI_PORTS = "10095/tcp,10095/udp"; }; volumes = [ "/srv/qbittorrent:/config/config" "/srv/video:/media/video" ]; }; radarr = lib.mkIf cfg.radarr.enable { hostname = "radarr"; image = "ghcr.io/hotio/radarr:release-5.9.1.9070"; autoStart = true; ports = [ "7878:7878/tcp" ]; extraOptions = [ "--network=${networkName}" ]; environment = { PUID = toString config.users.users.radarr.uid; inherit PGID UMASK; TZ = config.time.timeZone; }; volumes = [ # TODO "config:/config" # TODO "data:/data" ]; }; sonarr = lib.mkIf cfg.sonarr.enable { hostname = "sonarr"; image = "ghcr.io/hotio/sonarr:release-4.0.9.2244"; autoStart = true; ports = [ "8989:8989/tcp" ]; extraOptions = [ "--network=${networkName}" ]; environment = { PUID = toString config.users.users.sonarr.uid; inherit PGID UMASK; TZ = config.time.timeZone; }; volumes = [ # TODO "config:/config" # TODO "data:/data" ]; }; }; }; }