2021-12-05 20:28:49 +00:00
{ config , options , pkgs , lib , . . . }:
2020-03-24 11:45:50 +00:00
with lib ;
let
cfg = config . services . rtorrent ;
2021-12-05 20:28:49 +00:00
opt = options . services . rtorrent ;
2020-03-24 11:45:50 +00:00
in {
options . services . rtorrent = {
enable = mkEnableOption " r t o r r e n t " ;
dataDir = mkOption {
type = types . str ;
default = " / v a r / l i b / r t o r r e n t " ;
2022-07-28 22:19:15 +01:00
description = lib . mdDoc ''
2020-03-24 11:45:50 +00:00
The directory where rtorrent stores its data files .
'' ;
} ;
downloadDir = mkOption {
type = types . str ;
default = " ${ cfg . dataDir } / d o w n l o a d " ;
2021-12-05 20:28:49 +00:00
defaultText = literalExpression '' " ''$ { c o n f i g . ${ opt . dataDir } } / d o w n l o a d " '' ;
2022-07-28 22:19:15 +01:00
description = lib . mdDoc ''
2020-03-24 11:45:50 +00:00
Where to put downloaded files .
'' ;
} ;
user = mkOption {
type = types . str ;
default = " r t o r r e n t " ;
2022-07-28 22:19:15 +01:00
description = lib . mdDoc ''
2020-03-24 11:45:50 +00:00
User account under which rtorrent runs .
'' ;
} ;
group = mkOption {
type = types . str ;
default = " r t o r r e n t " ;
2022-07-28 22:19:15 +01:00
description = lib . mdDoc ''
2020-03-24 11:45:50 +00:00
Group under which rtorrent runs .
'' ;
} ;
package = mkOption {
type = types . package ;
default = pkgs . rtorrent ;
2021-10-03 17:06:03 +01:00
defaultText = literalExpression " p k g s . r t o r r e n t " ;
2022-07-28 22:19:15 +01:00
description = lib . mdDoc ''
2020-03-24 11:45:50 +00:00
The rtorrent package to use .
'' ;
} ;
port = mkOption {
type = types . port ;
default = 50000 ;
2022-07-28 22:19:15 +01:00
description = lib . mdDoc ''
2020-03-24 11:45:50 +00:00
The rtorrent port .
'' ;
} ;
openFirewall = mkOption {
type = types . bool ;
default = false ;
2022-07-28 22:19:15 +01:00
description = lib . mdDoc ''
Whether to open the firewall for the port in { option } ` services . rtorrent . port ` .
2020-03-24 11:45:50 +00:00
'' ;
} ;
rpcSocket = mkOption {
type = types . str ;
readOnly = true ;
default = " / r u n / r t o r r e n t / r p c . s o c k " ;
2022-07-28 22:19:15 +01:00
description = lib . mdDoc ''
2020-03-24 11:45:50 +00:00
RPC socket path .
'' ;
} ;
configText = mkOption {
type = types . lines ;
default = " " ;
2022-07-28 22:19:15 +01:00
description = lib . mdDoc ''
The content of { file } ` rtorrent . rc ` . The [ modernized configuration template ] ( https://rtorrent-docs.readthedocs.io/en/latest/cookbook.html #modernized-configuration-template) with the values specified in this module will be prepended using mkBefore. You can use mkForce to overwrite the config completly.
2020-03-24 11:45:50 +00:00
'' ;
} ;
} ;
config = mkIf cfg . enable {
users . groups = mkIf ( cfg . group == " r t o r r e n t " ) {
rtorrent = { } ;
} ;
users . users = mkIf ( cfg . user == " r t o r r e n t " ) {
rtorrent = {
group = cfg . group ;
shell = pkgs . bashInteractive ;
home = cfg . dataDir ;
description = " r t o r r e n t D a e m o n u s e r " ;
isSystemUser = true ;
} ;
} ;
networking . firewall . allowedTCPPorts = mkIf ( cfg . openFirewall ) [ cfg . port ] ;
services . rtorrent . configText = mkBefore ''
# Instance layout (base paths)
method . insert = cfg . basedir , private | const | string , ( cat , " ${ cfg . dataDir } / " )
method . insert = cfg . watch , private | const | string , ( cat , ( cfg . basedir ) , " w a t c h / " )
method . insert = cfg . logs , private | const | string , ( cat , ( cfg . basedir ) , " l o g / " )
method . insert = cfg . logfile , private | const | string , ( cat , ( cfg . logs ) , ( system . time ) , " . l o g " )
method . insert = cfg . rpcsock , private | const | string , ( cat , " ${ cfg . rpcSocket } " )
# Create instance directories
execute . throw = sh , - c , ( cat , " m k d i r - p " , ( cfg . basedir ) , " / s e s s i o n " , ( cfg . watch ) , " " , ( cfg . logs ) )
# Listening port for incoming peer traffic (fixed; you can also randomize it)
network . port_range . set = $ { toString cfg . port } - $ { toString cfg . port }
network . port_random . set = no
# Tracker-less torrent and UDP tracker support
# (conservative settings for 'private' trackers, change for 'public')
dht . mode . set = disable
protocol . pex . set = no
trackers . use_udp . set = no
# Peer settings
throttle . max_uploads . set = 100
throttle . max_uploads . global . set = 250
throttle . min_peers . normal . set = 20
throttle . max_peers . normal . set = 60
throttle . min_peers . seed . set = 30
throttle . max_peers . seed . set = 80
trackers . numwant . set = 80
protocol . encryption . set = allow_incoming , try_outgoing , enable_retry
# Limits for file handle resources, this is optimized for
# an `ulimit` of 1024 (a common default). You MUST leave
# a ceiling of handles reserved for rTorrent's internal needs!
network . http . max_open . set = 50
network . max_open_files . set = 600
network . max_open_sockets . set = 3000
# Memory resource usage (increase if you have a large number of items loaded,
# and/or the available resources to spend)
pieces . memory . max . set = 1 8 0 0 M
network . xmlrpc . size_limit . set = 4 M
# Basic operational settings (no need to change these)
session . path . set = ( cat , ( cfg . basedir ) , " s e s s i o n / " )
directory . default . set = " ${ cfg . downloadDir } "
log . execute = ( cat , ( cfg . logs ) , " e x e c u t e . l o g " )
##log.xmlrpc = (cat, (cfg.logs), "xmlrpc.log")
execute . nothrow = sh , - c , ( cat , " e c h o > " , ( session . path ) , " r t o r r e n t . p i d " , " " , ( system . pid ) )
# Other operational settings (check & adapt)
encoding . add = utf8
system . umask . set = 0027
system . cwd . set = ( cfg . basedir )
network . http . dns_cache_timeout . set = 25
schedule2 = monitor_diskspace , 15 , 60 , ( ( close_low_diskspace , 1 0 0 0 M ) )
# Watch directories (add more as you like, but use unique schedule names)
#schedule2 = watch_start, 10, 10, ((load.start, (cat, (cfg.watch), "start/*.torrent")))
#schedule2 = watch_load, 11, 10, ((load.normal, (cat, (cfg.watch), "load/*.torrent")))
# Logging:
# Levels = critical error warn notice info debug
# Groups = connection_* dht_* peer_* rpc_* storage_* thread_* tracker_* torrent_*
print = ( cat , " L o g g i n g t o " , ( cfg . logfile ) )
log . open_file = " l o g " , ( cfg . logfile )
log . add_output = " i n f o " , " l o g "
##log.add_output = "tracker_debug", "log"
# XMLRPC
scgi_local = ( cfg . rpcsock )
schedule = scgi_group , 0 , 0 , " e x e c u t e . n o t h r o w = c h o w n , \" : r t o r r e n t \" , ( c f g . r p c s o c k ) "
schedule = scgi_permission , 0 , 0 , " e x e c u t e . n o t h r o w = c h m o d , \" g + w , o = \" , ( c f g . r p c s o c k ) "
'' ;
systemd = {
services = {
rtorrent = let
rtorrentConfigFile = pkgs . writeText " r t o r r e n t . r c " cfg . configText ;
in {
description = " r T o r r e n t s y s t e m s e r v i c e " ;
after = [ " n e t w o r k . t a r g e t " ] ;
path = [ cfg . package pkgs . bash ] ;
wantedBy = [ " m u l t i - u s e r . t a r g e t " ] ;
serviceConfig = {
User = cfg . user ;
Group = cfg . group ;
Type = " s i m p l e " ;
Restart = " o n - f a i l u r e " ;
WorkingDirectory = cfg . dataDir ;
ExecStartPre = '' ${ pkgs . bash } / b i n / b a s h - c " i f t e s t - e ${ cfg . dataDir } / s e s s i o n / r t o r r e n t . l o c k & & t e s t - z $( ${ pkgs . procps } / b i n / p i d o f r t o r r e n t ) ; t h e n r m - f ${ cfg . dataDir } / s e s s i o n / r t o r r e n t . l o c k ; f i " '' ;
ExecStart = " ${ cfg . package } / b i n / r t o r r e n t - n - o s y s t e m . d a e m o n . s e t = t r u e - o i m p o r t = ${ rtorrentConfigFile } " ;
RuntimeDirectory = " r t o r r e n t " ;
RuntimeDirectoryMode = 755 ;
} ;
} ;
} ;
tmpfiles . rules = [ " d ' ${ cfg . dataDir } ' 0 7 5 0 ${ cfg . user } ${ cfg . group } - " ] ;
} ;
} ;
}