Skip to content

Commit dc16671

Browse files
authored
configure recyclarr with sane defaults (#13)
* configure recyclarr with sane defaults * fix url base * fix prowlarr, fix quality profiles * create main service from scratch * fix postgres file existence * fix radarr file renaming * fix sonarr quality definition includes * remove sqp radarr quality profile * fix linting * configure full defaults for sonarr and radarr * add cleanup unmanaged profiles option * minor tweaks and fixes * remove api waits * remove requisit from cleanup
1 parent ec24f9d commit dc16671

20 files changed

Lines changed: 1402 additions & 249 deletions

modules/arr-common/mkArrServiceModule.nix

Lines changed: 150 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,86 @@ with lib; let
1414
rootFolders = import ./rootFolders.nix {inherit lib pkgs serviceName;};
1515
downloadClients = import ./downloadClients.nix {inherit lib pkgs serviceName;};
1616
capitalizedName = toUpper (substring 0 1 serviceName) + substring 1 (-1) serviceName;
17+
screamingName = toUpper serviceName;
1718
usesMediaDirs = !(elem serviceName ["prowlarr"]);
18-
serviceSupportsUserGroup = !(elem serviceName ["prowlarr"]);
19+
mkServarrSettingsOptions = name:
20+
lib.mkOption {
21+
type = lib.types.submodule {
22+
freeformType = (pkgs.formats.ini {}).type;
23+
options = {
24+
update = {
25+
mechanism = lib.mkOption {
26+
type = with lib.types;
27+
nullOr (enum [
28+
"external"
29+
"builtIn"
30+
"script"
31+
]);
32+
description = "which update mechanism to use";
33+
default = "external";
34+
};
35+
automatically = lib.mkOption {
36+
type = lib.types.bool;
37+
description = "Automatically download and install updates.";
38+
default = false;
39+
};
40+
};
41+
server = {
42+
port = lib.mkOption {
43+
type = lib.types.port;
44+
description = "Port Number";
45+
};
46+
};
47+
log = {
48+
analyticsEnabled = lib.mkOption {
49+
type = lib.types.bool;
50+
description = "Send Anonymous Usage Data";
51+
default = false;
52+
};
53+
};
54+
};
55+
};
56+
example = lib.options.literalExpression ''
57+
{
58+
update.mechanism = "internal";
59+
server = {
60+
urlbase = "localhost";
61+
port = ${toString port};
62+
bindaddress = "*";
63+
};
64+
}
65+
'';
66+
default = {};
67+
description = ''
68+
Attribute set of arbitrary config options.
69+
Please consult the documentation at the [wiki](https://wiki.servarr.com/useful-tools#using-environment-variables-for-config).
70+
71+
WARNING: this configuration is stored in the world-readable Nix store!
72+
Don't put secrets here!
73+
'';
74+
};
75+
76+
mkServarrSettingsEnvVars = name: settings:
77+
lib.pipe settings [
78+
(lib.mapAttrsRecursive (
79+
path: value:
80+
lib.optionalAttrs (value != null) {
81+
name = lib.toUpper "${name}__${lib.concatStringsSep "__" path}";
82+
value = toString (
83+
if lib.isBool value
84+
then lib.boolToString value
85+
else value
86+
);
87+
}
88+
))
89+
(lib.collect (x: lib.isString x.name or false && lib.isString x.value or false))
90+
lib.listToAttrs
91+
];
1992
in {
2093
options.nixflix.${serviceName} =
2194
{
2295
enable = mkEnableOption "${capitalizedName}";
96+
package = lib.mkPackageOption pkgs serviceName {};
2397

2498
vpn = {
2599
enable = mkOption {
@@ -45,6 +119,14 @@ in {
45119
description = "Group under which the service runs";
46120
};
47121

122+
openFirewall = lib.mkOption {
123+
type = lib.types.bool;
124+
default = false;
125+
description = "Open ports in the firewall for the Radarr web interface.";
126+
};
127+
128+
settings = mkServarrSettingsOptions serviceName;
129+
48130
config = mkOption {
49131
type = types.submodule {
50132
options =
@@ -76,19 +158,7 @@ in {
76158
}
77159
// optionalAttrs usesMediaDirs {
78160
mediaDirs = mkOption {
79-
type = types.listOf (types.submodule {
80-
options = {
81-
dir = mkOption {
82-
type = types.str;
83-
description = "Directory path";
84-
};
85-
owner = mkOption {
86-
type = types.str;
87-
default = "root";
88-
description = "Directory owner";
89-
};
90-
};
91-
});
161+
type = types.listOf types.path;
92162
default = [];
93163
description = "List of media directories to create and manage";
94164
};
@@ -102,76 +172,52 @@ in {
102172
}
103173
];
104174

105-
nixflix.${serviceName}.config = {
106-
apiKeyPath = mkDefault null;
107-
hostConfig = {
108-
username = mkDefault serviceName;
109-
passwordPath = mkDefault null;
110-
instanceName = mkDefault capitalizedName;
111-
urlBase = mkDefault (
112-
if nixflix.serviceNameIsUrlBase
113-
then "/${serviceName}"
114-
else ""
175+
nixflix.${serviceName} = {
176+
settings = mkDefault ({
177+
auth = {
178+
required = "Enabled";
179+
method = "Forms";
180+
};
181+
server = {inherit (cfg.config.hostConfig) port urlBase;};
182+
}
183+
// optionalAttrs config.services.postgresql.enable {
184+
log.dbEnabled = true;
185+
postgres = {
186+
inherit (cfg) user;
187+
host = "/run/postgresql";
188+
port = 5432;
189+
mainDb = cfg.user;
190+
logDb = cfg.user;
191+
};
192+
});
193+
config = {
194+
apiKeyPath = mkDefault null;
195+
hostConfig = {
196+
username = mkDefault serviceName;
197+
passwordPath = mkDefault null;
198+
instanceName = mkDefault capitalizedName;
199+
urlBase = mkDefault (
200+
if nixflix.serviceNameIsUrlBase
201+
then "/${serviceName}"
202+
else ""
203+
);
204+
};
205+
downloadClients = mkDefault (
206+
optionals (config.nixflix.sabnzbd.enable or false) [
207+
{
208+
name = "SABnzbd";
209+
implementationName = "SABnzbd";
210+
inherit (config.nixflix.sabnzbd) apiKeyPath;
211+
inherit (config.nixflix.sabnzbd.settings) host;
212+
inherit (config.nixflix.sabnzbd.settings) port;
213+
urlBase = config.nixflix.sabnzbd.settings.url_base;
214+
}
215+
]
115216
);
116217
};
117-
downloadClients = mkDefault (
118-
optionals (config.nixflix.sabnzbd.enable or false) [
119-
{
120-
name = "SABnzbd";
121-
implementationName = "SABnzbd";
122-
inherit (config.nixflix.sabnzbd) apiKeyPath;
123-
inherit (config.nixflix.sabnzbd.settings) host;
124-
inherit (config.nixflix.sabnzbd.settings) port;
125-
urlBase = config.nixflix.sabnzbd.settings.url_base;
126-
}
127-
]
128-
);
129218
};
130219

131-
nixflix.dirRegistrations =
132-
[
133-
{
134-
inherit (cfg) group;
135-
dir = stateDir;
136-
owner = cfg.user;
137-
}
138-
]
139-
++ optionals usesMediaDirs (map (mediaDir: {
140-
inherit (cfg) group;
141-
inherit (mediaDir) dir owner;
142-
})
143-
cfg.mediaDirs);
144-
145220
services = {
146-
${serviceName} =
147-
{
148-
inherit (cfg) enable;
149-
dataDir = stateDir;
150-
}
151-
// optionalAttrs serviceSupportsUserGroup {
152-
inherit (cfg) user group;
153-
}
154-
// {
155-
settings =
156-
{
157-
auth = {
158-
required = "Enabled";
159-
method = "Forms";
160-
};
161-
server = {inherit (cfg.config.hostConfig) port urlBase;};
162-
}
163-
// optionalAttrs config.services.postgresql.enable {
164-
log.dbEnabled = true;
165-
postgres = {
166-
inherit (cfg) user;
167-
host = "/run/postgresql";
168-
port = 5432;
169-
mainDb = cfg.user;
170-
logDb = cfg.user;
171-
};
172-
};
173-
};
174-
175221
postgresql = mkIf config.services.postgresql.enable {
176222
ensureDatabases = [cfg.user];
177223
ensureUsers = [
@@ -207,13 +253,29 @@ in {
207253
users.${cfg.user} =
208254
{
209255
inherit (cfg) group;
256+
home = stateDir;
210257
isSystemUser = true;
211258
}
212259
// optionalAttrs (globals.uids ? ${cfg.user}) {
213260
uid = globals.uids.${cfg.user};
214261
};
215262
};
216263

264+
networking.firewall = lib.mkIf cfg.openFirewall {
265+
allowedTCPPorts = [cfg.config.hostConfig.port];
266+
};
267+
268+
systemd.tmpfiles = {
269+
settings."10-${serviceName}".${stateDir}.d = {
270+
inherit (cfg) user group;
271+
mode = "0700";
272+
};
273+
274+
rules =
275+
optionals usesMediaDirs (map (mediaDir: "d '${mediaDir}' 0770 ${globals.libraryOwner.user} ${globals.libraryOwner.group} - -")
276+
cfg.mediaDirs);
277+
};
278+
217279
systemd.services =
218280
{
219281
"${serviceName}-wait-for-db" = mkIf config.services.postgresql.enable {
@@ -243,8 +305,11 @@ in {
243305
};
244306

245307
${serviceName} = {
308+
description = capitalizedName;
309+
environment = mkServarrSettingsEnvVars screamingName cfg.settings;
310+
246311
after =
247-
["nixflix-setup-dirs.service"]
312+
["network.target" "nixflix-setup-dirs.service"]
248313
++ (optional (cfg.config.apiKeyPath != null && cfg.config.hostConfig.passwordPath != null) "${serviceName}-env.service")
249314
++ (optional config.services.postgresql.enable "postgresql-ready.target")
250315
++ (optional config.nixflix.mullvad.enable "mullvad-config.service");
@@ -253,19 +318,22 @@ in {
253318
++ (optional (cfg.config.apiKeyPath != null && cfg.config.hostConfig.passwordPath != null) "${serviceName}-env.service")
254319
++ (optional config.services.postgresql.enable "postgresql-ready.target");
255320
wants = optional config.nixflix.mullvad.enable "mullvad-config.service";
321+
wantedBy = ["multi-user.target"];
256322

257323
serviceConfig =
258324
{
259-
DynamicUser = mkForce false;
325+
Type = "simple";
260326
User = cfg.user;
261327
Group = cfg.group;
328+
ExecStart = "${getExe cfg.package} -nobrowser -data='${stateDir}'";
329+
Restart = "on-failure";
262330
}
263331
// optionalAttrs (cfg.config.apiKeyPath != null && cfg.config.hostConfig.passwordPath != null) {
264332
EnvironmentFile = "/run/${serviceName}/env";
265333
}
266334
// optionalAttrs (config.nixflix.mullvad.enable && !cfg.vpn.enable) {
267335
ExecStart = mkForce (pkgs.writeShellScript "${serviceName}-vpn-bypass" ''
268-
exec /run/wrappers/bin/mullvad-exclude ${getExe config.services.${serviceName}.package} \
336+
exec /run/wrappers/bin/mullvad-exclude ${getExe cfg.package} \
269337
-nobrowser -data='${stateDir}'
270338
'');
271339
AmbientCapabilities = "CAP_SYS_ADMIN";

modules/default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
./postgres.nix
88
./prowlarr
99
./radarr
10+
./recyclarr
1011
./sabnzbd
1112
./sonarr
1213
];

modules/lidarr/default.nix

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,15 @@ in {
1111

1212
config.nixflix.lidarr = {
1313
group = lib.mkDefault "media";
14-
mediaDirs = lib.mkDefault [
15-
{dir = "${nixflix.mediaDir}/music";}
16-
];
14+
mediaDirs = lib.mkDefault ["${nixflix.mediaDir}/music"];
1715
config = {
1816
apiVersion = lib.mkDefault "v1";
1917
hostConfig = {
2018
port = lib.mkDefault 8686;
2119
branch = lib.mkDefault "master";
2220
};
2321
rootFolders = lib.mkDefault (map (mediaDir: {
24-
path = mediaDir.dir;
22+
path = mediaDir;
2523
defaultQualityProfileId = 2;
2624
defaultMetadataProfileId = 1;
2725
defaultMonitorOption = "all";

modules/nixflix.nix

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -106,36 +106,6 @@ in {
106106
> Is not supported, because `/home/user` is owned by `user`.
107107
'';
108108
};
109-
110-
dirRegistrations = mkOption {
111-
type = with types;
112-
listOf (submodule {
113-
options = {
114-
dir = mkOption {
115-
type = path;
116-
description = "Directory path to create";
117-
};
118-
owner = mkOption {
119-
type = str;
120-
description = "Owner of the directory";
121-
};
122-
group = mkOption {
123-
type = str;
124-
description = "Group of the directory";
125-
};
126-
mode = mkOption {
127-
type = str;
128-
default = "0775";
129-
description = "Permission mode for the directory";
130-
};
131-
};
132-
});
133-
default = [];
134-
description = ''
135-
Directory registrations from services. Each service can register
136-
directories it needs to be created.
137-
'';
138-
};
139109
};
140110

141111
config = mkIf cfg.enable {
@@ -164,13 +134,6 @@ in {
164134
${pkgs.coreutils}/bin/mkdir -p ${cfg.downloadsDir}
165135
${pkgs.coreutils}/bin/chown ${globals.libraryOwner.user}:${globals.libraryOwner.group} ${cfg.downloadsDir}
166136
${pkgs.coreutils}/bin/chmod 0775 ${cfg.downloadsDir}
167-
168-
${concatMapStringsSep "\n" (reg: ''
169-
${pkgs.coreutils}/bin/mkdir -p ${reg.dir}
170-
${pkgs.coreutils}/bin/chown ${reg.owner}:${reg.group} ${reg.dir}
171-
${pkgs.coreutils}/bin/chmod ${reg.mode} ${reg.dir}
172-
'')
173-
cfg.dirRegistrations}
174137
'';
175138
};
176139

0 commit comments

Comments
 (0)