1# Borrowed graciously from https://github.com/WiredMic/nix-config/commit/d9268ce5190a2041ef66b492900eed278d1508e2#diff-9db90aeeaf81739c27dcdab8065abc8709d0bd5428bc658cff2db46acc91536a
2{
3 config,
4 lib,
5 pkgs,
6 utils,
7 ...
8}: let
9 name = "qbittorrent";
10 cfg = config.myNixOS.services.${name};
11
12 network = config.mySnippets.tailnet;
13 service = network.networkMap.${name};
14in {
15 options.myNixOS.services.qbittorrent = {
16 enable = lib.mkEnableOption "qBittorrent headless";
17
18 dataDir = lib.mkOption {
19 type = lib.types.path;
20 default = "/var/lib/qbittorrent";
21 description = "The directory where qBittorrent stores its data files.";
22 };
23
24 user = lib.mkOption {
25 type = lib.types.str;
26 default = "qbittorrent";
27 description = "User account under which qBittorrent runs.";
28 };
29
30 group = lib.mkOption {
31 type = lib.types.str;
32 default = "qbittorrent";
33 description = "Group under which qBittorrent runs.";
34 };
35
36 webuiPort = lib.mkOption {
37 type = lib.types.port;
38 default = 8080;
39 description = "qBittorrent web UI port.";
40 };
41
42 torrentingPort = lib.mkOption {
43 type = lib.types.port;
44 default = 16620;
45 description = "qBittorrent torrenting port.";
46 };
47
48 openFirewall = lib.mkOption {
49 type = lib.types.bool;
50 default = false;
51 description = "Open services.qBittorrent.port to the outside network.";
52 };
53
54 package = lib.mkOption {
55 type = lib.types.package;
56 default = pkgs.qbittorrent-nox;
57 defaultText = lib.literalExpression "pkgs.qbittorrent-nox";
58 description = "The qbittorrent package to use.";
59 };
60
61 extraArgs = lib.mkOption {
62 type = lib.types.listOf lib.types.str;
63 default = [];
64 description = ''
65 Extra arguments passed to qbittorrent. See `qbittorrent -h`, or the [source code](https://github.com/qbittorrent/qBittorrent/blob/master/src/app/cmdoptions.cpp), for the available arguments.
66 '';
67 example = [
68 "--confirm-legal-notice"
69 ];
70 };
71
72 autoProxy = lib.mkOption {
73 default = true;
74 example = false;
75 description = "${name} auto proxy";
76 type = lib.types.bool;
77 };
78 };
79
80 config = lib.mkIf cfg.enable {
81 networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall (
82 lib.optionals (cfg.webuiPort != null) [cfg.webuiPort]
83 ++ lib.optionals (cfg.torrentingPort != null) [cfg.torrentingPort]
84 );
85
86 services.caddy.virtualHosts."${service.vHost}".extraConfig = lib.mkIf cfg.autoProxy ''
87 bind tailscale/${name}
88 encode zstd gzip
89 reverse_proxy ${service.hostName}:${toString service.port}
90 '';
91
92 systemd.services.qbittorrent = {
93 after = ["local-fs.target" "network-online.target"];
94 description = "qBittorrent-nox service";
95 documentation = ["man:qbittorrent-nox(1)"];
96 requires = ["local-fs.target" "network-online.target"];
97 wantedBy = ["multi-user.target"];
98
99 serviceConfig = {
100 Type = "simple";
101 User = cfg.user;
102 Group = cfg.group;
103
104 # Run the pre-start script with full permissions (the "!" prefix) so it
105 # can create the data directory if necessary.
106 ExecStartPre = let
107 preStartScript = pkgs.writeScript "qbittorrent-run-prestart" ''
108 #!${pkgs.bash}/bin/bash
109
110 # Create data directory if it doesn't exist
111 if ! test -d "$QBT_PROFILE"; then
112 echo "Creating initial qBittorrent data directory in: $QBT_PROFILE"
113 install -d -m 0755 -o "${cfg.user}" -g "${cfg.group}" "$QBT_PROFILE"
114 fi
115 '';
116 in "!${preStartScript}";
117
118 ExecStart = utils.escapeSystemdExecArgs (
119 [
120 (lib.getExe cfg.package)
121 ]
122 ++ lib.optionals (cfg.torrentingPort != null) ["--torrenting-port=${toString cfg.torrentingPort}"]
123 ++ cfg.extraArgs
124 );
125 # To prevent "Quit & shutdown daemon" from working; we want systemd to
126 # manage it!
127 #Restart = "on-success";
128 #UMask = "0002";
129 #LimitNOFILE = cfg.openFilesLimit;
130 };
131
132 environment = {
133 QBT_PROFILE = cfg.dataDir;
134 QBT_WEBUI_PORT = toString cfg.webuiPort;
135 };
136 };
137
138 users = {
139 users = lib.mkIf (cfg.user == "qbittorrent") {
140 qbittorrent = {
141 inherit (cfg) group;
142 isSystemUser = true;
143 };
144 };
145 groups = lib.mkIf (cfg.group == "qbittorrent") {qbittorrent = {};};
146 };
147 };
148}