From 5b6130db0574b7526ea5834adb01fbe8b162a75a Mon Sep 17 00:00:00 2001 From: Michael Walker Date: Sat, 24 Aug 2024 18:18:43 +0100 Subject: [PATCH 1/2] Switch from rTorrent to Transmission I've noticed Flood becoming increasingly unstable, only being fixed (temporarily) by restarting rTorrent. Upon investigation, it looked like the rTorrent was getting into a bad state where it wouldn't respond to RPC requests. I couldn't get to the bottom of it, so I've switched the torrent backend out for Transmission, which works pretty much just as well. Flood is still pretty laggy, but at least does appear to keep working. --- flake.nix | 2 +- hosts/nyarlathotep/configuration.nix | 13 +-- .../restic-prepare--hardlink-torrent-files.py | 2 +- shared/default.nix | 2 +- shared/rtorrent/default.nix | 97 ------------------- shared/rtorrent/erase-your-darlings.nix | 21 ---- shared/torrents/default.nix | 81 ++++++++++++++++ shared/torrents/erase-your-darlings.nix | 12 +++ shared/{rtorrent => torrents}/options.nix | 62 +++++++----- 9 files changed, 141 insertions(+), 151 deletions(-) delete mode 100644 shared/rtorrent/default.nix delete mode 100644 shared/rtorrent/erase-your-darlings.nix create mode 100644 shared/torrents/default.nix create mode 100644 shared/torrents/erase-your-darlings.nix rename shared/{rtorrent => torrents}/options.nix (55%) diff --git a/flake.nix b/flake.nix index 3adb74c7..b0a91d91 100644 --- a/flake.nix +++ b/flake.nix @@ -124,7 +124,7 @@ ./shared/bookmarks/options.nix ./shared/umami/options.nix ./shared/concourse/options.nix - ./shared/rtorrent/options.nix + ./shared/torrents/options.nix ./shared/oci-containers/options.nix ./shared/pleroma/options.nix ./shared/resolved/options.nix diff --git a/hosts/nyarlathotep/configuration.nix b/hosts/nyarlathotep/configuration.nix index 5842528e..ab224f4b 100644 --- a/hosts/nyarlathotep/configuration.nix +++ b/hosts/nyarlathotep/configuration.nix @@ -245,7 +245,7 @@ in services.caddy.virtualHosts."flood.nyarlathotep.lan:80".extraConfig = '' import restrict_vlan encode gzip - reverse_proxy http://localhost:${toString config.nixfiles.rtorrent.flood.port} + reverse_proxy http://localhost:${toString config.nixfiles.torrents.flood.port} ''; services.caddy.virtualHosts."finder.nyarlathotep.lan:80".extraConfig = '' @@ -337,13 +337,14 @@ in ############################################################################### - ## rTorrent + ## torrents ############################################################################### - nixfiles.rtorrent.enable = true; - nixfiles.rtorrent.downloadDir = "/mnt/nas/torrents/files/"; - nixfiles.rtorrent.watchDir = "/mnt/nas/torrents/watch/"; - nixfiles.rtorrent.user = "barrucadu"; + nixfiles.torrents.enable = true; + nixfiles.torrents.downloadDir = "/mnt/nas/torrents/files"; + nixfiles.torrents.watchDir = "/mnt/nas/torrents/watch"; + nixfiles.torrents.user = "barrucadu"; + nixfiles.torrents.group = "users"; ############################################################################### diff --git a/hosts/nyarlathotep/jobs/restic-prepare--hardlink-torrent-files.py b/hosts/nyarlathotep/jobs/restic-prepare--hardlink-torrent-files.py index 4a8f95ce..6891ed10 100644 --- a/hosts/nyarlathotep/jobs/restic-prepare--hardlink-torrent-files.py +++ b/hosts/nyarlathotep/jobs/restic-prepare--hardlink-torrent-files.py @@ -11,7 +11,7 @@ # Directories to link MEDIA_DIRS = ["/mnt/nas/anime", "/mnt/nas/movies", "/mnt/nas/tv"] -# Where rtorrent downloads its files to +# Where torrent files are downloaded to TORRENT_FILES_DIR = "/mnt/nas/torrents/files" # Where .torrent files are stored diff --git a/shared/default.nix b/shared/default.nix index 0db6771f..c145c598 100644 --- a/shared/default.nix +++ b/shared/default.nix @@ -36,7 +36,7 @@ in ./pleroma ./resolved ./restic-backups - ./rtorrent + ./torrents ./umami ]; diff --git a/shared/rtorrent/default.nix b/shared/rtorrent/default.nix deleted file mode 100644 index 791e7cfb..00000000 --- a/shared/rtorrent/default.nix +++ /dev/null @@ -1,97 +0,0 @@ -# [rTorrent][] is a bittorrent client. This module configures it in a way -# appropriate for private trackers. -# -# This module does not include a backup script. Torrented files must be backed -# up independently. -# -# **Erase your darlings:** transparently stores session data and logs on the -# persistent volume. -# -# [rTorrent]: https://github.com/rakshasa/rtorrent -{ config, lib, pkgs, ... }: - -with lib; -let - cfg = config.nixfiles.rtorrent; - - stateDir = "/var/lib/rtorrent"; - logDir = "/var/log/rtorrent"; - rpcSocketPath = "/run/rtorrent/rpc.sock"; - - rtorrentrc = pkgs.writeText "rtorrent.rc" '' - # Paths - directory.default.set = ${cfg.downloadDir} - session.path.set = ${stateDir}/session/ - - # Logging - method.insert = cfg.logfile, private|const|string, (cat,"${logDir}/",(system.time),".log") - log.open_file = "log", (cfg.logfile) - ${concatMapStringsSep "\n" (lvl: "log.add_output = \"${lvl}\", \"log\"") cfg.logLevels} - - # Listening port for incoming peer traffic - network.port_range.set = ${toString cfg.portRange.from}-${toString cfg.portRange.to} - network.port_random.set = no - - # Optimise for private trackers (disable DHT & UDP trackers) - dht.mode.set = disable - protocol.pex.set = no - trackers.use_udp.set = no - - # Force encryption - protocol.encryption.set = allow_incoming,try_outgoing,require,require_RC4 - - # Write filenames in UTF-8 - encoding.add = UTF-8 - - # File options - pieces.hash.on_completion.set = yes - pieces.sync.always_safe.set = yes - - # Monitor for new .torrent files - schedule2 = watch_directory,5,5,load.start=${cfg.watchDir}*.torrent - - # XMLRPC - network.scgi.open_local = ${rpcSocketPath} - ''; - -in -{ - imports = [ - ./erase-your-darlings.nix - ./options.nix - ]; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPortRanges = mkIf cfg.openFirewall [ cfg.portRange ]; - - systemd.services.rtorrent = { - enable = true; - wantedBy = [ "multi-user.target" ]; - after = [ "network-online.target" ]; - wants = [ "network-online.target" ]; - serviceConfig = { - ExecStart = "${pkgs.rtorrent}/bin/rtorrent -n -o system.daemon.set=true -o import=${rtorrentrc}"; - User = cfg.user; - Restart = "on-failure"; - LogsDirectory = "rtorrent"; - RuntimeDirectory = "rtorrent"; - StateDirectory = "rtorrent/session"; - # with a lot of torrents, rtorrent can take a while to shut down - TimeoutStopSec = 300; - }; - }; - - systemd.services.flood = mkIf cfg.flood.enable { - enable = true; - wantedBy = [ "multi-user.target" ]; - after = [ "rtorrent.service" ]; - requires = [ "rtorrent.service" ]; - serviceConfig = { - ExecStart = "${pkgs.flood}/bin/flood --noauth --port=${toString cfg.flood.port} --rundir=${stateDir}/flood --rtsocket=${rpcSocketPath}"; - User = cfg.user; - Restart = "on-failure"; - StateDirectory = "rtorrent/flood"; - }; - }; - }; -} diff --git a/shared/rtorrent/erase-your-darlings.nix b/shared/rtorrent/erase-your-darlings.nix deleted file mode 100644 index 1b7195c3..00000000 --- a/shared/rtorrent/erase-your-darlings.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ config, lib, ... }: - -with lib; -let - cfg = config.nixfiles.rtorrent; - eyd = config.nixfiles.eraseYourDarlings; - - logDir = "/var/log/rtorrent"; - stateDir = "/var/lib/rtorrent"; -in -{ - config = mkIf (cfg.enable && eyd.enable) { - systemd.services.rtorrent.serviceConfig.BindPaths = [ - "${toString eyd.persistDir}${logDir}:${logDir}" - "${toString eyd.persistDir}${stateDir}/session:${stateDir}/session" - ]; - systemd.services.flood.serviceConfig.BindPaths = [ - "${toString eyd.persistDir}${stateDir}/flood:${stateDir}/flood" - ]; - }; -} diff --git a/shared/torrents/default.nix b/shared/torrents/default.nix new file mode 100644 index 00000000..1f81ca89 --- /dev/null +++ b/shared/torrents/default.nix @@ -0,0 +1,81 @@ +# [Transmission][] is a bittorrent client. This module configures it along with +# a web UI ([Flood][]). +# +# This module does not include a backup script. Torrented files must be backed +# up independently. +# +# **Erase your darlings:** transparently stores session data on the persistent +# volume. +# +# [Transmission]: https://transmissionbt.com/ +# [Flood]: https://flood.js.org/ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.nixfiles.torrents; +in +{ + imports = [ + ./erase-your-darlings.nix + ./options.nix + ]; + + config = mkIf cfg.enable { + services.transmission = { + enable = true; + user = cfg.user; + group = cfg.group; + home = "${cfg.stateDir}/transmission"; + openPeerPorts = cfg.openFirewall; + settings = { + # paths + download-dir = cfg.downloadDir; + watch-dir = cfg.watchDir; + watch-dir-enabled = true; + incomplete-dir-enabled = false; + + # optimise for private trackers (disable DHT and PEX, force encryption) + encryption = 2; + dht-enabled = false; + pex-enabled = false; + + # peers + peer-port = cfg.peerPort; + peer-port-random-on-start = false; + + # rpc + rpc-bind-address = "127.0.0.1"; + rpc-port = cfg.rpcPort; + + # misc + message-level = cfg.logLevel; + rename-partial-files = false; + trash-can-enabled = false; + trash-original-torrent-files = false; + }; + }; + + systemd.services.flood = mkIf cfg.flood.enable { + enable = true; + wantedBy = [ "multi-user.target" ]; + after = [ "transmission.service" ]; + requires = [ "transmission.service" ]; + serviceConfig = { + # flood needs `--truser` and `--trpass` passing even though the RPC + # socket is unauthenticated + ExecStart = concatStringsSep " " [ + "${pkgs.flood}/bin/flood " + "--noauth " + "--port=${toString cfg.flood.port} " + "--rundir=${cfg.stateDir}/flood " + "--trurl=http://127.0.0.1:${toString cfg.rpcPort}/transmission/rpc" + "--truser=transmission" + "--trpass=transmission" + ]; + User = cfg.user; + Restart = "on-failure"; + }; + }; + }; +} diff --git a/shared/torrents/erase-your-darlings.nix b/shared/torrents/erase-your-darlings.nix new file mode 100644 index 00000000..367490bc --- /dev/null +++ b/shared/torrents/erase-your-darlings.nix @@ -0,0 +1,12 @@ +{ config, lib, ... }: + +with lib; +let + cfg = config.nixfiles.torrents; + eyd = config.nixfiles.eraseYourDarlings; +in +{ + config = mkIf (cfg.enable && eyd.enable) { + nixfiles.torrents.stateDir = "${toString eyd.persistDir}/var/lib/torrents"; + }; +} diff --git a/shared/rtorrent/options.nix b/shared/torrents/options.nix similarity index 55% rename from shared/rtorrent/options.nix rename to shared/torrents/options.nix index e4c83a2d..d1ba7d5f 100644 --- a/shared/rtorrent/options.nix +++ b/shared/torrents/options.nix @@ -3,26 +3,34 @@ with lib; { - options.nixfiles.rtorrent = { + options.nixfiles.torrents = { enable = mkOption { type = types.bool; default = false; description = mdDoc '' - Enable the [rTorrent](https://github.com/rakshasa/rtorrent) service. + Enable the [Transmission](https://transmissionbt.com/) service. ''; }; downloadDir = mkOption { type = types.str; - example = "/mnt/nas/torrents/files/"; + example = "/mnt/nas/torrents/files"; description = mdDoc '' Directory to download torrented files to. ''; }; + stateDir = mkOption { + type = types.str; + example = "/var/lib/torrents"; + description = mdDoc '' + Directory to store service state in. + ''; + }; + watchDir = mkOption { type = types.str; - example = "/mnt/nas/torrents/watch/"; + example = "/mnt/nas/torrents/watch"; description = mdDoc '' Directory to monitor for new .torrent files. ''; @@ -31,13 +39,20 @@ with lib; user = mkOption { type = types.str; description = mdDoc '' - The user to run rTorrent as. + The user to run Transmission as. ''; }; - logLevels = mkOption { - type = types.listOf types.str; - default = [ "info" ]; + group = mkOption { + type = types.str; + description = mdDoc '' + The group to run Transmission as. + ''; + }; + + logLevel = mkOption { + type = types.ints.between 0 6; + default = 2; description = mdDoc '' Verbosity of the log messages. ''; @@ -52,21 +67,20 @@ with lib; ''; }; - portRange = { - from = mkOption { - type = types.int; - default = 50000; - description = mdDoc '' - Lower bound (inclusive) of the port range to accept connections on. - ''; - }; - to = mkOption { - type = types.int; - default = 50000; - description = mdDoc '' - Upper bound (inclusive) of the port range to accept connections on. - ''; - }; + peerPort = mkOption { + type = types.port; + default = 50000; + description = mdDoc '' + Port to accept peer connections on. + ''; + }; + + rpcPort = mkOption { + type = types.port; + default = 49528; + description = mdDoc '' + Port to accept RPC connections on. Bound on 127.0.0.1. + ''; }; flood = { @@ -78,7 +92,7 @@ with lib; ''; }; port = mkOption { - type = types.int; + type = types.port; default = 45904; description = mdDoc '' Port (on 127.0.0.1) to expose Flood on. From 1ea305b8cb655672749b0aaab5626040cf499382 Mon Sep 17 00:00:00 2001 From: Michael Walker Date: Sat, 24 Aug 2024 22:44:17 +0100 Subject: [PATCH 2/2] Retire flood in favour of transmission web UI It performs way better. --- hosts/nyarlathotep/configuration.nix | 2 +- shared/torrents/default.nix | 27 +++------------------------ shared/torrents/options.nix | 17 ----------------- 3 files changed, 4 insertions(+), 42 deletions(-) diff --git a/hosts/nyarlathotep/configuration.nix b/hosts/nyarlathotep/configuration.nix index ab224f4b..19ea6ce7 100644 --- a/hosts/nyarlathotep/configuration.nix +++ b/hosts/nyarlathotep/configuration.nix @@ -245,7 +245,7 @@ in services.caddy.virtualHosts."flood.nyarlathotep.lan:80".extraConfig = '' import restrict_vlan encode gzip - reverse_proxy http://localhost:${toString config.nixfiles.torrents.flood.port} + reverse_proxy http://localhost:${toString config.nixfiles.torrents.rpcPort} ''; services.caddy.virtualHosts."finder.nyarlathotep.lan:80".extraConfig = '' diff --git a/shared/torrents/default.nix b/shared/torrents/default.nix index 1f81ca89..c699474c 100644 --- a/shared/torrents/default.nix +++ b/shared/torrents/default.nix @@ -1,5 +1,5 @@ # [Transmission][] is a bittorrent client. This module configures it along with -# a web UI ([Flood][]). +# a web UI. # # This module does not include a backup script. Torrented files must be backed # up independently. @@ -8,7 +8,6 @@ # volume. # # [Transmission]: https://transmissionbt.com/ -# [Flood]: https://flood.js.org/ { config, lib, pkgs, ... }: with lib; @@ -28,6 +27,7 @@ in group = cfg.group; home = "${cfg.stateDir}/transmission"; openPeerPorts = cfg.openFirewall; + webHome = pkgs.flood-for-transmission; settings = { # paths download-dir = cfg.downloadDir; @@ -47,6 +47,7 @@ in # rpc rpc-bind-address = "127.0.0.1"; rpc-port = cfg.rpcPort; + rpc-host-whitelist-enabled = false; # misc message-level = cfg.logLevel; @@ -55,27 +56,5 @@ in trash-original-torrent-files = false; }; }; - - systemd.services.flood = mkIf cfg.flood.enable { - enable = true; - wantedBy = [ "multi-user.target" ]; - after = [ "transmission.service" ]; - requires = [ "transmission.service" ]; - serviceConfig = { - # flood needs `--truser` and `--trpass` passing even though the RPC - # socket is unauthenticated - ExecStart = concatStringsSep " " [ - "${pkgs.flood}/bin/flood " - "--noauth " - "--port=${toString cfg.flood.port} " - "--rundir=${cfg.stateDir}/flood " - "--trurl=http://127.0.0.1:${toString cfg.rpcPort}/transmission/rpc" - "--truser=transmission" - "--trpass=transmission" - ]; - User = cfg.user; - Restart = "on-failure"; - }; - }; }; } diff --git a/shared/torrents/options.nix b/shared/torrents/options.nix index d1ba7d5f..13507afc 100644 --- a/shared/torrents/options.nix +++ b/shared/torrents/options.nix @@ -82,22 +82,5 @@ with lib; Port to accept RPC connections on. Bound on 127.0.0.1. ''; }; - - flood = { - enable = mkOption { - type = types.bool; - default = true; - description = mdDoc '' - Enable the [Flood](https://flood.js.org/) web UI. - ''; - }; - port = mkOption { - type = types.port; - default = 45904; - description = mdDoc '' - Port (on 127.0.0.1) to expose Flood on. - ''; - }; - }; }; }