diff --git a/docs/wiki/examples/example-1/index.md b/docs/wiki/examples/example-1/index.md index 97f9ac5..50ee5a3 100644 --- a/docs/wiki/examples/example-1/index.md +++ b/docs/wiki/examples/example-1/index.md @@ -49,6 +49,6 @@ This example does the following: radarr.enable = true; readarr.enable = true; sonarr.enable = true; + jellyseerr.enable = true; }; ``` - diff --git a/docs/wiki/examples/example-2/index.md b/docs/wiki/examples/example-2/index.md index 49e44f9..d1ebeba 100644 --- a/docs/wiki/examples/example-2/index.md +++ b/docs/wiki/examples/example-2/index.md @@ -51,6 +51,7 @@ example does the following: prowlarr.enable = true; readarr.enable = true; lidarr.enable = true; + jellyseerr.enable = true; }; # The `openssh.vpn.enable` option does not enable openssh, so we do that here: diff --git a/flake.lock b/flake.lock index 57fd46b..9aa3f0e 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1730831018, - "narHash": "sha256-2S0HwIFRxYp+afuoFORcZA9TjryAf512GmE0MTfEOPU=", + "lastModified": 1736657626, + "narHash": "sha256-FWlPMUzp0lkQBdhKlPqtQdqmp+/C+1MBiEytaYfrCTY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "8c4dc69b9732f6bbe826b5fbb32184987520ff26", + "rev": "2f9e2f85cb14a46410a1399aa9ea7ecf433e422e", "type": "github" }, "original": { @@ -24,11 +24,11 @@ }, "vpnconfinement": { "locked": { - "lastModified": 1729977304, - "narHash": "sha256-T/ABQpSbPJxO7TGl1P2fBd87xsQmnVflIFgHQTxwvBs=", + "lastModified": 1731209328, + "narHash": "sha256-b3jggBHZh20jUfBxoaIvew23czsw82zBc0aKxtkF3g8=", "owner": "Maroka-chan", "repo": "VPN-Confinement", - "rev": "3b11ba59f1c852493203b86c8acd715259a6c3cd", + "rev": "74e6fd47804b5ca69187200efbb14cf1ecb9ea07", "type": "github" }, "original": { diff --git a/nixarr/default.nix b/nixarr/default.nix index 6ea61fd..b9b5664 100644 --- a/nixarr/default.nix +++ b/nixarr/default.nix @@ -71,11 +71,16 @@ with lib; let + strings.optionalString cfg.readarr.enable '' chown -R readarr:root "${cfg.readarr.stateDir}" find "${cfg.readarr.stateDir}" \( -type d -exec chmod 0700 {} + -true \) -o \( -exec chmod 0600 {} + \) + '' + + strings.optionalString cfg.jellyseerr.enable '' + chown -R jellyseerr:root "${cfg.jellyseerr.stateDir}" + find "${cfg.jellyseerr.stateDir}" \( -type d -exec chmod 0700 {} + -true \) -o \( -exec chmod 0600 {} + \) ''; }; in { imports = [ ./jellyfin + ./jellyseerr ./bazarr ./ddns ./radarr @@ -114,6 +119,7 @@ in { The following services are supported: - [Jellyfin](#nixarr.jellyfin.enable) + - [Jellyseerr](#nixarr.jellyseerr.enable) - [Bazarr](#nixarr.bazarr.enable) - [Lidarr](#nixarr.lidarr.enable) - [Prowlarr](#nixarr.prowlarr.enable) @@ -124,6 +130,9 @@ in { - [SABnzbd](#nixarr.sabnzbd.enable) Remember to read the options. + + > **Warning:** The Jellyseerr module currently does not work on nixos 24.11. + > You will have to update to the `unstable` branch in order for it to work. ''; }; diff --git a/nixarr/jellyseerr/default.nix b/nixarr/jellyseerr/default.nix new file mode 100644 index 0000000..494352a --- /dev/null +++ b/nixarr/jellyseerr/default.nix @@ -0,0 +1,140 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixarr.jellyseerr; + nixarr = config.nixarr; + defaultPort = 5055; +in { + imports = [ + ./jellyseerr-module + ]; + + options.nixarr.jellyseerr = { + enable = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + Whether or not to enable the Jellyseerr service. + + **Required options:** [`nixarr.enable`](#nixarr.enable) + ''; + }; + + package = mkPackageOption pkgs "jellyseerr" {}; + + stateDir = mkOption { + type = types.path; + default = "${nixarr.stateDir}/jellyseerr"; + defaultText = literalExpression ''"''${nixarr.stateDir}/jellyseerr"''; + example = "/nixarr/.state/jellyseerr"; + description = '' + The location of the state directory for the Jellyseerr service. + + > **Warning:** Setting this to any path, where the subpath is not + > owned by root, will fail! For example: + > + > ```nix + > stateDir = /home/user/nixarr/.state/jellyseerr + > ``` + > + > Is not supported, because `/home/user` is owned by `user`. + ''; + }; + + port = mkOption { + type = types.port; + default = defaultPort; + example = 12345; + description = "Jellyseerr web-UI port."; + }; + + openFirewall = mkOption { + type = types.bool; + defaultText = literalExpression ''!nixarr.jellyseerr.vpn.enable''; + default = !cfg.vpn.enable; + example = true; + description = "Open firewall for Jellyseerr"; + }; + + vpn.enable = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + **Required options:** [`nixarr.vpn.enable`](#nixarr.vpn.enable) + + Route Jellyseerr traffic through the VPN. + ''; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.enable -> nixarr.enable; + message = '' + The nixarr.jellyseerr.enable option requires the + nixarr.enable option to be set, but it was not. + ''; + } + { + assertion = cfg.vpn.enable -> nixarr.vpn.enable; + message = '' + The nixarr.jellyseerr.vpn.enable option requires the + nixarr.vpn.enable option to be set, but it was not. + ''; + } + ]; + + util-nixarr.services.jellyseerr = { + enable = true; + package = cfg.package; + openFirewall = cfg.openFirewall; + port = cfg.port; + configDir = cfg.stateDir; + }; + + # Enable and specify VPN namespace to confine service in. + systemd.services.jellyseerr.vpnConfinement = mkIf cfg.vpn.enable { + enable = true; + vpnNamespace = "wg"; + }; + + # Port mappings + vpnNamespaces.wg = mkIf cfg.vpn.enable { + portMappings = [ + { + from = defaultPort; + to = defaultPort; + } + ]; + }; + + services.nginx = mkIf cfg.vpn.enable { + enable = true; + + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + + virtualHosts."127.0.0.1:${builtins.toString defaultPort}" = { + listen = [ + { + addr = "0.0.0.0"; + port = defaultPort; + } + ]; + locations."/" = { + recommendedProxySettings = true; + proxyWebsockets = true; + proxyPass = "http://192.168.15.1:${builtins.toString defaultPort}"; + }; + }; + }; + }; +} diff --git a/nixarr/jellyseerr/jellyseerr-module/default.nix b/nixarr/jellyseerr/jellyseerr-module/default.nix new file mode 100644 index 0000000..fc6b77c --- /dev/null +++ b/nixarr/jellyseerr/jellyseerr-module/default.nix @@ -0,0 +1,89 @@ +{ + config, + pkgs, + lib, + ... +}: +with lib; let + cfg = config.util-nixarr.services.jellyseerr; +in { + options = { + util-nixarr.services.jellyseerr = { + enable = mkEnableOption "Jellyseerr"; + + package = mkPackageOption pkgs "jellyseerr" {}; + + user = mkOption { + type = types.str; + default = "jellyseerr"; + description = "User account under which Jellyseerr runs."; + }; + + group = mkOption { + type = types.str; + default = "jellyseerr"; + description = "Group under which Jellyseerr runs."; + }; + + configDir = mkOption { + type = types.str; + default = "/var/lib/jellyseerr"; + description = "The directory where Jellyseerr stores its config data."; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 5055; + description = ''The port which the Jellyseerr web UI should listen to.''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = "Open ports in the firewall for the Jellyseerr web interface."; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.tmpfiles.rules = [ + "d '${cfg.configDir}' 0700 ${cfg.user} ${cfg.group} - -" + ]; + + systemd.services.jellyseerr = { + description = "Jellyseerr, a requests manager for Jellyfin"; + after = ["network.target"]; + wantedBy = ["multi-user.target"]; + environment = { + PORT = toString cfg.port; + CONFIG_DIRECTORY = cfg.configDir; + }; + + serviceConfig = { + Type = "exec"; + StateDirectory = "jellyseerr"; + DynamicUser = false; + User = cfg.user; + Group = cfg.group; + ExecStart = lib.getExe cfg.package; + Restart = "on-failure"; + }; + }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [5055]; + }; + + users.users = mkIf (cfg.user == "jellyseerr") { + jellyseerr = { + group = cfg.group; + home = cfg.configDir; + uid = 294; + }; + }; + + users.groups = mkIf (cfg.group == "jellyseerr") { + jellyseerr = {}; + }; + }; +}