Compare commits

...

24 Commits

Author SHA1 Message Date
Alexandra Østermark 7cc521933d Merge pull request #142 from vicgeentor/fix-package-option
Deploy / run-nix-build (push) Has been cancelled
Deploy / deploy-site (push) Has been cancelled
Lint / fmt (push) Has been cancelled
Lint / build-website (push) Has been cancelled
Lint / test (push) Has been cancelled
fix: use cfg.package instead of pkgs.bazarr in the bazarr module
2026-02-08 10:14:26 +01:00
vicgeentor c95a949b12 fix: use cfg.package instead of pkgs.bazarr in the bazarr module 2026-02-07 21:50:24 +01:00
Alexandra Østermark 96973b6a8c Merge pull request #140 from nix-media-server/org-changes
Org changes
2026-02-02 15:21:43 +01:00
rasmus-kirk ec44dbe4ff Org changes 2026-02-02 07:06:30 +01:00
Rasmus Kirk 204da9209a Merge pull request #115 from cramt/cramt/flake_update
flake update
2025-12-14 16:56:44 +00:00
Alexandra Østermark d6838844ca fixed 2025-12-14 16:46:46 +01:00
Alexandra Østermark b4b30f6c78 flake update 2025-12-14 16:03:20 +01:00
rasmus-kirk ac11a6d765 Added check for building website 2025-12-02 10:57:55 +01:00
Rasmus Kirk 3ea7ad8a1c Merge pull request #107 from cramt/fix_transmission_failing_in_ci
fix transmission failing in ci
2025-11-25 10:20:22 +00:00
Alexandra Østermark be6d4f50cd fix transmission failing in ci 2025-11-24 14:34:11 +01:00
Rasmus Kirk 66d0dcb3c3 Merge pull request #98 from pierzchalski/eap/rationalise-readarr
`readarr{,-audiobook}`: use `service.${service}` config pattern
2025-11-23 08:10:04 +00:00
Rasmus Kirk 86f6e7b617 Merge pull request #105 from sunaurus/fix/recyclarr-accept-strings
fix: allow strings in recyclarr yaml type
2025-11-23 08:05:13 +00:00
Edward Pierzchalski 4d1d1cfe76 lib, readarr-audiobook: copy *arr helpers from nixpkgs instead of downloading them 2025-11-22 10:00:09 +11:00
sunaurus c2af6384bd fix: recyclarr only match tags on strings 2025-11-21 18:18:40 +02:00
Sander Saarend 32d4c7002c fix: allow strings in recyclarr yaml type 2025-11-21 12:39:30 +02:00
rasmus-kirk 45f8838f3c Merge branch 'main' of github.com:rasmus-kirk/nixarr 2025-11-15 15:32:42 +01:00
rasmus-kirk eab22b92fa minor revision 2025-11-15 15:30:32 +01:00
Edward Pierzchalski d8ca911a13 [readarr-audiobook] fix one call of mkServarr utility functions, document which calls need readarr vs readarr-audiobook and why 2025-11-14 10:48:44 +11:00
Rasmus Kirk fd055b3af0 Merge pull request #100 from cramt/preserve_env_var_tags_for_recyclarr_configuration
preserve env var yaml tags for recyclarr config
2025-11-13 14:52:56 +00:00
Alexandra Østermark 74307df8e3 comments descriping the unreadable yaml generation 2025-11-13 11:30:23 +01:00
Alexandra Østermark 9a4dc02b20 fix minor mistake 2025-11-12 20:10:17 +01:00
Alexandra Østermark 709ab63d5a preserve env var yaml tags for recyclarr config 2025-11-12 19:55:42 +01:00
Edward Pierzchalski 4b2316b18a readarr-audiobook: add service.readarr-audiobook to mirror other *arr services
This lets us use `services.readarr-audiobook` to configure it the same
way we can use `services.{readarr,radarr,sonarr,prowlarr}`.
2025-11-11 10:12:22 +11:00
Edward Pierzchalski a5007d9d37 readarr: use the NixOS module as much as possible
See also this PR doing something similar for `prowlarr`: https://github.com/rasmus-kirk/nixarr/pull/95
2025-11-11 10:12:22 +11:00
16 changed files with 382 additions and 140 deletions
+12 -6
View File
@@ -13,21 +13,27 @@ jobs:
fmt:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/checkout@v5
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
uses: DeterminateSystems/nix-installer-action@v3
- run: nix fmt -- --check .
build-website:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v3
- run: nix build
test:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
-1
View File
@@ -5,7 +5,6 @@
Added:
- `whisparr` service
- `komgarr` service
- `stash` service
Fixed:
- Cross-seed now uses `transmission` user.
+1 -1
View File
@@ -66,7 +66,7 @@ like shown in this example flake:
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
nixarr.url = "github:rasmus-kirk/nixarr";
nixarr.url = "github:nix-media-server/nixarr";
};
outputs = {
+4 -3
View File
@@ -31,10 +31,11 @@ option, which by default is set to `/data/media`.
## Transmission
Transmission should already be setup and running since it's configured
with JSON, and can therefore be configured with nix. The most basic settings are already set. See the following links for more info:
Transmission should already be setup and running since it's configured with
JSON, and can therefore be configured with nix. The most basic settings are
already set. See the following links for more info:
- [The configured Nixarr defaults for transmission](https://github.com/rasmus-kirk/nixarr/blob/28d1be070deb1a064c1967889c11c8921752fa09/nixarr/transmission/default.nix#L355)
- [The configured Nixarr defaults for transmission](https://github.com/nix-media-server/nixarr/blob/28d1be070deb1a064c1967889c11c8921752fa09/nixarr/transmission/default.nix#L355)
- [The `nixarr.transmission` options](https://nixarr.com/nixos-options/#nixarr.transmission.enable)
- [Settings that can be passed through `nixarr.transmission.settings`]
-3
View File
@@ -29,9 +29,6 @@ Then you can set that port for a service, for example
## Debugging Ports
> **Note:** See [this GH issue](https://github.com/rasmus-kirk/nixarr/issues/27)
> first, since it's a common problem
You can debug an open port using the
[nixarr.vpn.vpnTestService](https://nixarr.com/options.html#nixarr.vpn.vpntestservice.enable):
Generated
+7 -7
View File
@@ -2,16 +2,16 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1761016216,
"narHash": "sha256-G/iC4t/9j/52i/nm+0/4ybBmAF4hzR8CNHC75qEhjHo=",
"lastModified": 1765608474,
"narHash": "sha256-9Wx53UK0z8Di5iesJID0tS1dRKwGxI4i7tsSanOHhF0=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "481cf557888e05d3128a76f14c76397b7d7cc869",
"rev": "28bb483c11a1214a73f9fd2d9928a6e2ea86ec71",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-25.05",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
@@ -25,11 +25,11 @@
},
"vpnconfinement": {
"locked": {
"lastModified": 1759956062,
"narHash": "sha256-NUZu0Rb0fwUjfdp51zMm0xM3lcK8Kw4c97LLog7+JjA=",
"lastModified": 1765634578,
"narHash": "sha256-Fujb9sn1cj+u/bzfo2RbQkcAvJ7Ch1pimJzFie4ptb4=",
"owner": "Maroka-chan",
"repo": "VPN-Confinement",
"rev": "fabe7247b720b5eb4c3c053e24a2b3b70e64c52b",
"rev": "f2989e1e3cb06c7185939e9ddc368f88b998616a",
"type": "github"
},
"original": {
+22 -11
View File
@@ -2,7 +2,7 @@
description = "The Nixarr Media Server Nixos Module";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05";
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
vpnconfinement.url = "github:Maroka-chan/VPN-Confinement";
@@ -27,20 +27,26 @@
# Helper to provide system-specific attributes
forAllSystems = f:
nixpkgs.lib.genAttrs supportedSystems (system:
nixpkgs.lib.genAttrs supportedSystems (
system:
f {
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
});
}
);
in {
nixosModules.default.imports = [./nixarr vpnconfinement.nixosModules.default];
nixosModules.default.imports = [
./nixarr
vpnconfinement.nixosModules.default
];
# Add tests attribute to the flake outputs
# To run interactively run:
# > nix build .#checks.x86_64-linux.monitoring-test.driver -L
checks = forAllSystems ({pkgs}: {
checks = forAllSystems (
{pkgs}: {
permissions-test = pkgs.callPackage ./tests/permissions-test.nix {
inherit (self) nixosModules;
};
@@ -50,18 +56,22 @@
# vpn-confinement-test = pkgs.callPackage ./tests/vpn-confinement-test.nix {
# inherit (self) nixosModules;
# };
});
}
);
devShells = forAllSystems ({pkgs}: {
devShells = forAllSystems (
{pkgs}: {
default = pkgs.mkShell {
packages = with pkgs; [
alejandra
nixd
];
};
});
}
);
packages = forAllSystems ({pkgs}: let
packages = forAllSystems (
{pkgs}: let
website = website-builder.lib {
pkgs = pkgs;
src = "${self}";
@@ -91,7 +101,7 @@
}
{
title = "Github";
location = "https://github.com/rasmus-kirk/nixarr";
location = "https://github.com/nix-media-server/nixarr";
}
];
favicons = {
@@ -111,7 +121,8 @@
in {
default = website.package;
debug = website.loop;
});
}
);
formatter = forAllSystems ({pkgs}: pkgs.alejandra);
};
+1 -1
View File
@@ -93,7 +93,7 @@ in {
Group = globals.bazarr.group;
SyslogIdentifier = "bazarr";
ExecStart = pkgs.writeShellScript "start-bazarr" ''
${pkgs.bazarr}/bin/bazarr \
${cfg.package}/bin/bazarr \
--config '${cfg.stateDir}' \
--port ${toString cfg.port} \
--no-update True
+93
View File
@@ -0,0 +1,93 @@
# Utilities for defining *arr service settings, options, and configs.
#
# Copied from nixpkgs master as of 2025-11-11: https://raw.githubusercontent.com/NixOS/nixpkgs/cf540f8c9840457ed90a315dd635bceecb78495a/nixos/modules/services/misc/servarr/settings-options.nix
{
lib,
pkgs,
}: {
mkServarrSettingsOptions = name: port:
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.port;
description = "Port Number";
default = port;
};
};
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 = ${toString port};
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!
For secrets use [](#opt-services.${name}.environmentFiles).
'';
};
mkServarrEnvironmentFiles = name:
lib.mkOption {
type = lib.types.listOf lib.types.path;
default = [];
description = ''
Environment file to pass secret configuration values.
Each line must follow the `${lib.toUpper name}__SECTION__KEY=value` pattern.
Please consult the documentation at the [wiki](https://wiki.servarr.com/useful-tools#using-environment-variables-for-config).
'';
};
mkServarrSettingsEnvVars = name: settings:
lib.pipe settings [
(lib.mapAttrsRecursive (
path: value:
lib.optionalAttrs (value != null) {
name = lib.toUpper "${name}__${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
];
}
+60 -4
View File
@@ -6,9 +6,12 @@
}:
with lib; let
cfg = config.nixarr.readarr-audiobook;
service-cfg = config.services.readarr-audiobook;
globals = config.util-nixarr.globals;
nixarr = config.nixarr;
port = 9494;
arr-settings-options = import ../lib/arr-settings-options.nix {inherit lib pkgs;};
in {
options.nixarr.readarr-audiobook = {
enable = mkOption {
@@ -69,6 +72,46 @@ in {
};
};
# A tweaked copy of services.readarr from nixpkgs
options.services.readarr-audiobook = {
enable = lib.mkEnableOption "Readarr-Audiobook, a Usenet/BitTorrent audiobook downloader";
dataDir = lib.mkOption {
type = lib.types.str;
description = "The directory where Readarr-Audiobook stores its data files.";
};
package = lib.mkPackageOption pkgs "readarr" {};
openFirewall = lib.mkOption {
type = lib.types.bool;
description = ''
Open ports in the firewall for Readarr-Audiobook.
'';
};
# Uses name in description to refer to
# `services.readarr-audiobook.environmentFiles`.
settings = arr-settings-options.mkServarrSettingsOptions "readarr-audiobook" port;
# Uses name in description to document `READARR__*` environment variables.
environmentFiles = arr-settings-options.mkServarrEnvironmentFiles "readarr";
user = lib.mkOption {
type = lib.types.str;
description = ''
User account under which Readarr-Audiobook runs.
'';
};
group = lib.mkOption {
type = lib.types.str;
description = ''
Group under which Readarr-Audiobook runs.
'';
};
};
config = mkIf (nixarr.enable && cfg.enable) {
assertions = [
{
@@ -96,17 +139,30 @@ in {
"d '${nixarr.mediaDir}/library/audiobooks' 0775 ${globals.libraryOwner.user} ${globals.libraryOwner.group} - -"
];
services.readarr-audiobook = {
enable = cfg.enable;
package = cfg.package;
settings.server.port = cfg.port;
openFirewall = cfg.openFirewall;
dataDir = cfg.stateDir;
user = globals.readarr-audiobook.user;
group = globals.readarr-audiobook.group;
};
systemd.services.readarr-audiobook = {
description = "Readarr-Audiobook";
after = ["network.target"];
wantedBy = ["multi-user.target"];
environment.READARR__SERVER__PORT = builtins.toString cfg.port;
# Uses name to define `READARR__*` environment variables.
environment = arr-settings-options.mkServarrSettingsEnvVars "readarr" service-cfg.settings;
serviceConfig = {
Type = "simple";
User = globals.readarr-audiobook.user;
Group = globals.readarr-audiobook.group;
ExecStart = "${lib.getExe cfg.package} -nobrowser -data=${cfg.stateDir}";
User = service-cfg.user;
Group = service-cfg.group;
EnvironmentFile = service-cfg.environmentFiles;
ExecStart = "${lib.getExe service-cfg.package} -nobrowser -data=${service-cfg.dataDir}";
Restart = "on-failure";
};
};
+8 -13
View File
@@ -94,19 +94,14 @@ in {
"d '${nixarr.mediaDir}/library/books' 0775 ${globals.libraryOwner.user} ${globals.libraryOwner.group} - -"
];
systemd.services.readarr = {
description = "Readarr";
after = ["network.target"];
wantedBy = ["multi-user.target"];
environment.READARR__SERVER__PORT = builtins.toString cfg.port;
serviceConfig = {
Type = "simple";
User = globals.readarr.user;
Group = globals.readarr.group;
ExecStart = "${lib.getExe cfg.package} -nobrowser -data=${cfg.stateDir}";
Restart = "on-failure";
};
services.readarr = {
enable = cfg.enable;
package = cfg.package;
settings.server.port = cfg.port;
openFirewall = cfg.openFirewall;
dataDir = cfg.stateDir;
user = globals.readarr.user;
group = globals.readarr.group;
};
networking.firewall = mkIf cfg.openFirewall {
+51 -1
View File
@@ -8,7 +8,57 @@ with lib; let
cfg = config.nixarr.recyclarr;
globals = config.util-nixarr.globals;
nixarr = config.nixarr;
format = pkgs.formats.yaml {};
# This is a carbon copy of the yaml implementation in nixpkgs https://github.com/NixOS/nixpkgs/blob/fde6c4aec177afa2d0248b1c5983e2a72a231442/pkgs/pkgs-lib/formats.nix#L210-L231
# except we've replaced json2yaml for yq-go to allow it to parse custom yaml tags
# ideally this would some day be upstreamed, see https://github.com/NixOS/nix/issues/4910 and https://github.com/nix-media-server/nixarr/issues/91
yamlGenerator = {preserved-tags ? []}: let
selectors =
pkgs.lib.strings.concatStringsSep "|"
(builtins.map (
# this is yq for "for all the scalers, if they match this regex, do a regex substitution and set the tag"
x: ''
with((.. | select(kind == "scalar") | select(tag == "!!str") | select(test("^!${x} .*"))); . = sub("!${x} ", "") | . tag="!${x}")
''
)
preserved-tags);
in {
generate = name: value:
pkgs.callPackage (
{
runCommand,
yq-go,
}:
runCommand name
{
nativeBuildInputs = [yq-go];
value = builtins.toJSON value;
passAsFile = ["value"];
preferLocalBuild = true;
}
''
yq '${selectors}' "$valuePath" -o yaml > $out
''
) {};
type = let
baseType = pkgs.lib.types.oneOf [
pkgs.lib.types.bool
pkgs.lib.types.int
pkgs.lib.types.float
pkgs.lib.types.str
(pkgs.lib.types.attrsOf valueType)
(pkgs.lib.types.listOf valueType)
];
valueType =
(pkgs.lib.types.nullOr baseType)
// {
description = "Yaml value";
};
in
valueType;
};
format = yamlGenerator {
preserved-tags = ["env_var"];
};
# Generate configuration file from Nix attribute set if provided
generatedConfigFile = format.generate "recyclarr-config.yml" cfg.configuration;
+11
View File
@@ -29,6 +29,11 @@ with lib; let
runtimeInputs = with pkgs; [yq];
text = ''
while [ ! -f "${nixarr.prowlarr.stateDir}/config.xml" ]; do
echo "Waiting for prowlarr to start..."
sleep 1
done
touch ${cfg.stateDir}/prowlarr-api-key
chmod 400 ${cfg.stateDir}/prowlarr-api-key
chown ${globals.transmission.user} ${cfg.stateDir}/prowlarr-api-key
@@ -49,6 +54,12 @@ with lib; let
+ ''
TMP_JSON=$(mktemp)
CRED_FILE="/run/secrets/cross-seed/credentialsFile.json"
while [ ! -f "${nixarr.prowlarr.stateDir}/config.xml" ]; do
echo "Waiting for prowlarr to start..."
sleep 1
done
PROWLARR_API_KEY=$(xq -r '.Config.ApiKey' "${nixarr.prowlarr.stateDir}/config.xml")
# shellcheck disable=SC2034
CRED_DIR=$(dirname "$CRED_FILE")
+1 -1
View File
@@ -4,7 +4,7 @@
nixosModules,
lib ? pkgs.lib,
}:
pkgs.nixosTest {
pkgs.testers.nixosTest {
name = "nixarr-permissions-test";
nodes.machine = {
+1 -1
View File
@@ -3,7 +3,7 @@
nixosModules,
lib ? pkgs.lib,
}:
pkgs.nixosTest {
pkgs.testers.nixosTest {
name = "simple-test";
nodes.machine = {
+33 -10
View File
@@ -43,17 +43,25 @@ The test ensures that:
wgGatewayPort = 51820;
# Generate real WireGuard keys
wgGatewayPrivateKey = pkgs.runCommand "wg-gateway-private" {buildInputs = [pkgs.wireguard-tools];} ''
wgGatewayPrivateKey =
pkgs.runCommand "wg-gateway-private" {buildInputs = [pkgs.wireguard-tools];}
''
wg genkey > $out
'';
wgGatewayPublicKey = pkgs.runCommand "wg-gateway-public" {buildInputs = [pkgs.wireguard-tools];} ''
wgGatewayPublicKey =
pkgs.runCommand "wg-gateway-public" {buildInputs = [pkgs.wireguard-tools];}
''
cat ${wgGatewayPrivateKey} | wg pubkey > $out
'';
wgClientPrivateKey = pkgs.runCommand "wg-client-private" {buildInputs = [pkgs.wireguard-tools];} ''
wgClientPrivateKey =
pkgs.runCommand "wg-client-private" {buildInputs = [pkgs.wireguard-tools];}
''
wg genkey > $out
'';
wgClientPublicKey = pkgs.runCommand "wg-client-public" {buildInputs = [pkgs.wireguard-tools];} ''
wgClientPublicKey =
pkgs.runCommand "wg-client-public" {buildInputs = [pkgs.wireguard-tools];}
''
cat ${wgClientPrivateKey} | wg pubkey > $out
'';
@@ -92,7 +100,7 @@ The test ensures that:
PersistentKeepalive = 25
'';
in
pkgs.nixosTest {
pkgs.testers.nixosTest {
name = "nixarr-vpn-confinement-test";
# Disable interactive mode to avoid hanging
@@ -128,7 +136,10 @@ in
"${internetClientIP}/24"
"${internetClientIPv6}/64"
];
gateway = ["${internetGatewayIP}" "${internetGatewayIPv6}"];
gateway = [
"${internetGatewayIP}"
"${internetGatewayIPv6}"
];
routes = [
{
Destination = "${wgSubnet}";
@@ -189,7 +200,10 @@ in
pkgs,
...
}: {
virtualisation.vlans = [1 2]; # VLAN 1 for LAN, VLAN 2 for Internet
virtualisation.vlans = [
1
2
]; # VLAN 1 for LAN, VLAN 2 for Internet
networking = {
interfaces.eth1 = {
@@ -224,19 +238,28 @@ in
firewall = {
enable = true;
allowedUDPPorts = [wgGatewayPort 51413];
allowedUDPPorts = [
wgGatewayPort
51413
];
allowedTCPPorts = [51413];
};
wireguard.interfaces.wg0 = {
ips = ["${wgGatewayAddr}/24" "${wgGatewayAddrV6}/64"];
ips = [
"${wgGatewayAddr}/24"
"${wgGatewayAddrV6}/64"
];
listenPort = wgGatewayPort;
privateKeyFile = "${wgGatewayPrivateKey}";
peers = [
{
publicKey = builtins.readFile wgClientPublicKey;
allowedIPs = ["${wgClientAddr}/32" "${wgClientAddrV6}/128"];
allowedIPs = [
"${wgClientAddr}/32"
"${wgClientAddrV6}/128"
];
}
];
};