{ 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 = lib.mkIf inUse { apps.enable = true; media.enable = true; }; # "Master switch": Enable all apps. apps.arr = lib.mkIf cfg.enable { bazarr.enable = true; lidarr.enable = true; prowlarr.enable = true; qbittorrent.enable = true; radarr.enable = true; sonarr.enable = true; }; fileSystems.media.video = { enable = true; permissions = [ "read" "write" ]; }; virtualisation.containers.enable = lib.mkIf inUse true; }; fileSystems = lib.mkIf inUse { "/srv/bazarr-backup" = lib.mkIf cfg.bazarr.enable { device = "192.168.0.11:/mnt/BIG/BACKUP/BAZARR"; fsType = "nfs"; options = [ "rw" "auto" "nfsvers=4.2" "rsize=1048576" "wsize=1048576" "hard" "timeo=600" "retrans=2" "_netdev" "nosuid" "tcp" ]; }; "/srv/lidarr-backup" = lib.mkIf cfg.lidarr.enable { device = "192.168.0.11:/mnt/BIG/BACKUP/LIDARR"; fsType = "nfs"; options = [ "rw" "auto" "nfsvers=4.2" "rsize=1048576" "wsize=1048576" "hard" "timeo=600" "retrans=2" "_netdev" "nosuid" "tcp" ]; }; "/srv/prowlarr-backup" = lib.mkIf cfg.prowlarr.enable { device = "192.168.0.11:/mnt/BIG/BACKUP/PROWLARR"; fsType = "nfs"; options = [ "rw" "auto" "nfsvers=4.2" "rsize=1048576" "wsize=1048576" "hard" "timeo=600" "retrans=2" "_netdev" "nosuid" "tcp" ]; }; "/srv/qbittorrent" = lib.mkIf cfg.qbittorrent.enable { device = "192.168.0.11:/mnt/SMALL/CONFIG/QBITTORRENT"; fsType = "nfs"; options = [ "rw" "auto" "nfsvers=4.2" "rsize=1048576" "wsize=1048576" "hard" "timeo=600" "retrans=2" "_netdev" "nosuid" "tcp" ]; }; "/srv/radarr-backup" = lib.mkIf cfg.radarr.enable { device = "192.168.0.11:/mnt/BIG/BACKUP/RADARR"; fsType = "nfs"; options = [ "rw" "auto" "nfsvers=4.2" "rsize=1048576" "wsize=1048576" "hard" "timeo=600" "retrans=2" "_netdev" "nosuid" "tcp" ]; }; "/srv/sonarr-backup" = lib.mkIf cfg.sonarr.enable { device = "192.168.0.11:/mnt/BIG/BACKUP/SONARR"; fsType = "nfs"; options = [ "rw" "auto" "nfsvers=4.2" "rsize=1048576" "wsize=1048576" "hard" "timeo=600" "retrans=2" "_netdev" "nosuid" "tcp" ]; }; "/srv/torrent" = { device = "192.168.0.11:/mnt/SMALL/MEDIA/TORRENT"; fsType = "nfs"; options = [ "rw" "auto" "nfsvers=4.2" "rsize=1048576" "wsize=1048576" "hard" "timeo=600" "retrans=2" "_netdev" "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; extraGroups = [ config.users.groups.apps.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 = let videoHostPath = config.homelab.fileSystems.media.video.hostPath; in { bazarr = let port = 6767; in lib.mkIf cfg.bazarr.enable { hostname = "bazarr"; image = "ghcr.io/hotio/bazarr:release-1.4.4"; autoStart = true; ports = [ # Open ports if you don't use Traefik # "${toString port}:${toString port}/tcp" # "${toString port}:${toString port}/udp" ]; extraOptions = [ "--network=${networkName}" ]; environment = { PUID = toString config.users.users.bazarr.uid; inherit PGID UMASK; TZ = config.time.timeZone; WEBUI_PORTS = "${toString port}/tcp,${toString port}/udp"; }; volumes = [ "bazarr-config:/config" "/srv/bazarr-backup:/config/backup" "${videoHostPath}/Films:/media/movies" "${videoHostPath}/Series:/media/series" ]; labels = { "traefik.enable" = "true"; "traefik.http.routers.bazarr.rule" = "Host(`bazarr.depeuter.dev`)"; "traefik.http.services.bazarr.loadbalancer.server.port" = toString port; }; }; lidarr = let port = 8686; in lib.mkIf cfg.lidarr.enable { hostname = "lidarr"; image = "ghcr.io/hotio/lidarr:release-2.5.3.4341"; autoStart = true; ports = [ # Open ports if you don't use Traefik # "${toString port}:${toString port}/tcp" ]; extraOptions = [ "--network=${networkName}" ]; environment = { PUID = toString config.users.users.lidarr.uid; inherit PGID UMASK; TZ = config.time.timeZone; }; volumes = [ "lidarr-config:/config" # TODO Fix path "/srv/lidarr-backup:/media/Backups" ]; }; prowlarr = let port = 9696; in lib.mkIf cfg.prowlarr.enable { hostname = "prowlarr"; image = "ghcr.io/hotio/prowlarr:release-1.23.1.4708"; autoStart = true; ports = [ # Open ports if you don't use Traefik # "${toString port}:${toString port}/tcp" ]; extraOptions = [ "--network=${networkName}" ]; environment = { PUID = toString config.users.users.prowlarr.uid; inherit PGID UMASK; TZ = config.time.timeZone; }; volumes = [ "prowlarr-config:/config" "/srv/prowlarr-backup:/config/Backups" ]; labels = { "traefik.enable" = "true"; "traefik.http.routers.prowlarr.rule" = "Host(`prowlarr.depeuter.dev`)"; "traefik.http.services.prowlarr.loadbalancer.server.port" = toString port; }; }; qbittorrent = let port = 10095; in lib.mkIf cfg.qbittorrent.enable { hostname = "qbittorrent"; image = "ghcr.io/hotio/qbittorrent:release-4.6.7"; autoStart = true; ports = [ # Open ports if you don't use Traefik # "${toString port}:${toString port}/tcp" # "${toString port}:${toString port}/udp" ]; extraOptions = [ "--network=${networkName}" ]; environment = { PUID = toString config.users.users.qbittorrent.uid; inherit PGID UMASK; TZ = config.time.timeZone; WEBUI_PORTS = "${toString port}/tcp,${toString port}/udp"; }; volumes = [ "/srv/qbittorrent:/config" "/srv/torrent:/media/cache" ]; labels = { "traefik.enable" = "true"; "traefik.http.routers.qbittorrent.rule" = "Host(`qb.depeuter.dev`)"; "traefik.http.services.qbittorrent.loadbalancer.server.port" = toString port; }; }; radarr = let port = 7878; in lib.mkIf cfg.radarr.enable { hostname = "radarr"; image = "ghcr.io/hotio/radarr:release-5.9.1.9070"; autoStart = true; ports = [ # Open ports if you don't use Traefik # "${toString port}:${toString port}/tcp" ]; extraOptions = [ "--network=${networkName}" ]; environment = { PUID = toString config.users.users.radarr.uid; inherit PGID UMASK; TZ = config.time.timeZone; }; volumes = [ "radarr-config:/config" "/srv/radarr-backup:/config/Backups" "/srv/torrent:/media/cache" "${videoHostPath}/Films:/media/movies" ]; labels = { "traefik.enable" = "true"; "traefik.http.routers.radarr.rule" = "Host(`radarr.depeuter.dev`)"; "traefik.http.services.radarr.loadbalancer.server.port" = toString port; }; }; sonarr = let port = 8989; in lib.mkIf cfg.sonarr.enable { hostname = "sonarr"; image = "ghcr.io/hotio/sonarr:release-4.0.9.2244"; autoStart = true; ports = [ # Open ports if you don't use Traefik # "${toString port}:${toString port}/tcp" ]; extraOptions = [ "--network=${networkName}" ]; environment = { PUID = toString config.users.users.sonarr.uid; inherit PGID UMASK; TZ = config.time.timeZone; }; volumes = [ "sonarr-config:/config" "/srv/sonarr-backup:/config/Backups" "/srv/torrent:/media/cache" "${videoHostPath}/Series:/media/series" ]; labels = { "traefik.enable" = "true"; "traefik.http.routers.sonarr.rule" = "Host(`sonarr.depeuter.dev`)"; "traefik.http.services.sonarr.loadbalancer.server.port" = toString port; }; }; }; }; }