1 { config, lib, pkgs, ... }:
6 cfg = config.services.deluge;
7 cfg_web = config.services.deluge.web;
8 isDeluge1 = versionOlder cfg.package.version "2.0.0";
10 openFilesLimit = 4096;
11 listenPortsDefault = [ 6881 6889 ];
13 listToRange = x: { from = elemAt x 0; to = elemAt x 1; };
15 configDir = "${cfg.dataDir}/.config/deluge";
16 configFile = pkgs.writeText "core.conf" (builtins.toJSON cfg.config);
17 declarativeLockFile = "${configDir}/.declarative";
19 preStart = if cfg.declarative then ''
20 if [ -e ${declarativeLockFile} ]; then
21 # Was declarative before, no need to back up anything
22 ${if isDeluge1 then "ln -sf" else "cp"} ${configFile} ${configDir}/core.conf
23 ln -sf ${cfg.authFile} ${configDir}/auth
25 # Declarative for the first time, backup stateful files
26 ${if isDeluge1 then "ln -s" else "cp"} -b --suffix=.stateful ${configFile} ${configDir}/core.conf
27 ln -sb --suffix=.stateful ${cfg.authFile} ${configDir}/auth
28 echo "Autogenerated file that signifies that this server configuration is managed declaratively by NixOS" \
29 > ${declarativeLockFile}
32 if [ -e ${declarativeLockFile} ]; then
33 rm ${declarativeLockFile}
40 enable = mkEnableOption (lib.mdDoc "Deluge daemon");
42 openFilesLimit = mkOption {
43 default = openFilesLimit;
44 type = types.either types.int types.str;
45 description = lib.mdDoc ''
46 Number of files to allow deluged to open.
53 example = literalExpression ''
55 download_location = "/srv/torrents/";
56 max_upload_speed = "1000.0";
57 share_ratio_limit = "2.0";
60 listen_ports = [ ${toString listenPortsDefault} ];
63 description = lib.mdDoc ''
64 Deluge core configuration for the core.conf file. Only has an effect
65 when {option}`services.deluge.declarative` is set to
66 `true`. String values must be quoted, integer and
67 boolean values must not. See
68 <https://git.deluge-torrent.org/deluge/tree/deluge/core/preferencesmanager.py#n41>
69 for the availaible options.
73 declarative = mkOption {
76 description = lib.mdDoc ''
77 Whether to use a declarative deluge configuration.
78 Only if set to `true`, the options
79 {option}`services.deluge.config`,
80 {option}`services.deluge.openFirewall` and
81 {option}`services.deluge.authFile` will be
86 openFirewall = mkOption {
89 description = lib.mdDoc ''
90 Whether to open the firewall for the ports in
91 {option}`services.deluge.config.listen_ports`. It only takes effet if
92 {option}`services.deluge.declarative` is set to
95 It does NOT apply to the daemon port nor the web UI port. To access those
96 ports secuerly check the documentation
97 <https://dev.deluge-torrent.org/wiki/UserGuide/ThinClient#CreateSSHTunnel>
98 or use a VPN or configure certificates for deluge.
104 default = "/var/lib/deluge";
105 description = lib.mdDoc ''
106 The directory where deluge will create files.
110 authFile = mkOption {
112 example = "/run/keys/deluge-auth";
113 description = lib.mdDoc ''
114 The file managing the authentication for deluge, the format of this
115 file is straightforward, each line contains a
116 username:password:level tuple in plaintext. It only has an effect
117 when {option}`services.deluge.declarative` is set to
119 See <https://dev.deluge-torrent.org/wiki/UserGuide/Authentication> for
127 description = lib.mdDoc ''
128 User account under which deluge runs.
135 description = lib.mdDoc ''
136 Group under which deluge runs.
140 extraPackages = mkOption {
141 type = types.listOf types.package;
143 description = lib.mdDoc ''
144 Extra packages available at runtime to enable Deluge's plugins. For example,
145 extraction utilities are required for the built-in "Extractor" plugin.
146 This always contains unzip, gnutar, xz and bzip2.
151 type = types.package;
152 example = literalExpression "pkgs.deluge-2_x";
153 description = lib.mdDoc ''
154 Deluge package to use.
160 enable = mkEnableOption (lib.mdDoc "Deluge Web daemon");
165 description = lib.mdDoc ''
170 openFirewall = mkOption {
173 description = lib.mdDoc ''
174 Open ports in the firewall for deluge web daemon
181 config = mkIf cfg.enable {
183 services.deluge.package = mkDefault (
184 if versionAtLeast config.system.stateVersion "20.09" then
187 # deluge-1_x is no longer packaged and this will resolve to an error
188 # thanks to the alias for this name. This is left here so that anyone
189 # using NixOS older than 20.09 receives that error when they upgrade
190 # and is forced to make an intentional choice to switch to deluge-2_x.
191 # That might be slightly inconvenient but there is no path to
192 # downgrade from 2.x to 1.x so NixOS should not automatically perform
193 # this state migration.
197 # Provide a default set of `extraPackages`.
198 services.deluge.extraPackages = with pkgs; [ unzip gnutar xz bzip2 ];
200 systemd.tmpfiles.rules = [
201 "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group}"
202 "d '${cfg.dataDir}/.config' 0770 ${cfg.user} ${cfg.group}"
203 "d '${cfg.dataDir}/.config/deluge' 0770 ${cfg.user} ${cfg.group}"
205 ++ optional (cfg.config ? download_location)
206 "d '${cfg.config.download_location}' 0770 ${cfg.user} ${cfg.group}"
207 ++ optional (cfg.config ? torrentfiles_location)
208 "d '${cfg.config.torrentfiles_location}' 0770 ${cfg.user} ${cfg.group}"
209 ++ optional (cfg.config ? move_completed_path)
210 "d '${cfg.config.move_completed_path}' 0770 ${cfg.user} ${cfg.group}";
212 systemd.services.deluged = {
213 after = [ "network.target" ];
214 description = "Deluge BitTorrent Daemon";
215 wantedBy = [ "multi-user.target" ];
216 path = [ cfg.package ] ++ cfg.extraPackages;
219 ${cfg.package}/bin/deluged \
221 --config ${configDir}
223 # To prevent "Quit & shutdown daemon" from working; we want systemd to
225 Restart = "on-success";
229 LimitNOFILE = cfg.openFilesLimit;
234 systemd.services.delugeweb = mkIf cfg_web.enable {
235 after = [ "network.target" "deluged.service"];
236 requires = [ "deluged.service" ];
237 description = "Deluge BitTorrent WebUI";
238 wantedBy = [ "multi-user.target" ];
239 path = [ cfg.package ];
242 ${cfg.package}/bin/deluge-web \
243 ${optionalString (!isDeluge1) "--do-not-daemonize"} \
244 --config ${configDir} \
245 --port ${toString cfg.web.port}
252 networking.firewall = mkMerge [
253 (mkIf (cfg.declarative && cfg.openFirewall && !(cfg.config.random_port or true)) {
254 allowedTCPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
255 allowedUDPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
257 (mkIf (cfg.web.openFirewall) {
258 allowedTCPPorts = [ cfg.web.port ];
262 environment.systemPackages = [ cfg.package ];
264 users.users = mkIf (cfg.user == "deluge") {
267 uid = config.ids.uids.deluge;
269 description = "Deluge Daemon user";
273 users.groups = mkIf (cfg.group == "deluge") {
275 gid = config.ids.gids.deluge;