diff --git a/nixos/modules/services/misc/sonarr.nix b/nixos/modules/services/misc/sonarr.nix
index 65f43df08312..b11293cbd27a 100644
--- a/nixos/modules/services/misc/sonarr.nix
+++ b/nixos/modules/services/misc/sonarr.nix
@@ -7,6 +7,18 @@
}:
let
cfg = config.services.sonarr;
+
+ settingsEnvVars = lib.pipe cfg.settings [
+ (lib.mapAttrsRecursive (
+ path: value:
+ lib.optionalAttrs (value != null) {
+ name = lib.toUpper "SONARR__${lib.concatStringsSep "__" path}";
+ value = toString (if lib.isBool value then lib.boolToString value else value);
+ }
+ ))
+ (lib.collect (x: lib.isString x.name or false && lib.isString x.value or false))
+ lib.listToAttrs
+ ];
in
{
options = {
@@ -27,6 +39,85 @@ in
'';
};
+ apiKeyFile = lib.mkOption {
+ type = lib.types.nullOr lib.types.path;
+ description = ''
+ Path to the file containing the API key for Sonarr (32 chars).
+ This will overwrite the API key in the config file.
+ '';
+ example = "/run/secrets/sonarr-apikey";
+ default = null;
+ };
+
+ environmentFiles = lib.mkOption {
+ type = lib.types.listOf lib.types.path;
+ default = [ ];
+ description = ''
+ Environment file to pass secret configuration values.
+
+ Each line must follow the `SONARR__SECTION__KEY=value` pattern.
+ Please consult the documentation at the [wiki](https://wiki.servarr.com/useful-tools#using-environment-variables-for-config).
+ '';
+ };
+
+ settings = lib.mkOption {
+ type = lib.types.submodule {
+ freeformType = (pkgs.formats.ini { }).type;
+ options = {
+ update = {
+ mechanism = lib.mkOption {
+ type =
+ with lib.types;
+ nullOr (enum [
+ "external"
+ "builtIn"
+ "script"
+ ]);
+ description = "which update mechanism to use";
+ default = "external";
+ };
+ automatically = lib.mkOption {
+ type = lib.types.bool;
+ description = "Automatically download and install updates.";
+ default = false;
+ };
+ };
+ server = {
+ port = lib.mkOption {
+ type = lib.types.int;
+ description = "Port Number";
+ default = 8989;
+ };
+ };
+ log = {
+ analyticsEnabled = lib.mkOption {
+ type = lib.types.bool;
+ description = "Send Anonymous Usage Data";
+ default = false;
+ };
+ };
+ };
+ };
+ example = lib.options.literalExpression ''
+ {
+ update.mechanism = "internal";
+ server = {
+ urlbase = "localhost";
+ port = 8989;
+ bindaddress = "*";
+ };
+ }
+ '';
+ default = { };
+ description = ''
+ Attribute set of arbitrary config options.
+ Please consult the documentation at the [wiki](https://wiki.servarr.com/useful-tools#using-environment-variables-for-config).
+
+ WARNING: this configuration is stored in the world-readable Nix store!
+ Use [](#opt-services.sonarr.environmentFiles) if it contains a secret.
+ '';
+ };
+
user = lib.mkOption {
type = lib.types.str;
default = "sonarr";
@@ -44,6 +135,15 @@ in
};
config = lib.mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = !(cfg.settings ? auth && cfg.settings.auth ? apikey);
+ message = ''
+ The `services.sonarr.settings` attribute set must not contain `ApiKey`, you should instead use `services.sonarr.apiKeyFile` to avoid storing secrets in the Nix store.
+ '';
+ }
+ ];
+
systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0700 ${cfg.user} ${cfg.group} - -"
];
@@ -52,11 +152,20 @@ in
description = "Sonarr";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
-
+ environment = settingsEnvVars;
serviceConfig = {
Type = "simple";
User = cfg.user;
Group = cfg.group;
+ EnvironmentFile = cfg.environmentFiles;
+ LoadCredential = lib.optional (cfg.apiKeyFile != null) "SONARR__AUTH__APIKEY:${cfg.apiKeyFile}";
+ ExecStartPre = lib.optionalString (cfg.apiKeyFile != null) pkgs.writeShellScript "sonarr-apikey" ''
+ if [ ! -s config.xml ]; then
+ echo '' > "${cfg.dataDir}/config.xml"
+ fi
+ ${lib.getExe pkgs.xmlstarlet} ed -L -u "/Config/ApiKey" -v "@API_KEY@" "${cfg.dataDir}/config.xml"
+ ${lib.getExe pkgs.replace-secret} '@API_KEY@' ''${CREDENTIALS_DIRECTORY}/SONARR__AUTH__APIKEY "${cfg.dataDir}/config.xml"
+ '';
ExecStart = utils.escapeSystemdExecArgs [
(lib.getExe cfg.package)
"-nobrowser"
@@ -67,7 +176,7 @@ in
};
networking.firewall = lib.mkIf cfg.openFirewall {
- allowedTCPPorts = [ 8989 ];
+ allowedTCPPorts = [ cfg.settings.server.port ];
};
users.users = lib.mkIf (cfg.user == "sonarr") {
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index be11bddb9329..7b2b757f6479 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -356,36 +356,18 @@ let
'';
};
- exportarr-sonarr = let apikey = "eccff6a992bc2e4b88e46d064b26bb4e"; in {
+ exportarr-sonarr = let apiKeyFile = pkgs.writeText "dummy-api-key" "eccff6a992bc2e4b88e46d064b26bb4e"; in {
nodeName = "exportarr_sonarr";
exporterConfig = {
enable = true;
url = "http://127.0.0.1:8989";
- apiKeyFile = pkgs.writeText "dummy-api-key" apikey;
+ inherit apiKeyFile;
};
metricProvider = {
- services.sonarr.enable = true;
- systemd.services.sonarr.serviceConfig.ExecStartPre =
- let
- sonarr_config = pkgs.writeText "config.xml" ''
-
- info
- False
- 8989
- 9898
-
- *
- ${apikey}
- None
- BuiltIn
- main
- Sonarr
-
- '';
- in
- [
- ''${pkgs.coreutils}/bin/install -D -m 777 ${sonarr_config} -T /var/lib/sonarr/.config/NzbDrone/config.xml''
- ];
+ services.sonarr = {
+ enable = true;
+ inherit apiKeyFile;
+ };
};
exporterTest = ''
wait_for_unit("sonarr.service")