me like nix
1{ inputs, ... }:
2
3let
4 jellyfinKodiSyncQueue = inputs.nixpkgs.legacyPackages.x86_64-linux.fetchzip {
5 url = "https://repo.jellyfin.org/releases/plugin/kodi-sync-queue/kodi-sync-queue_15.0.0.0.zip";
6 stripRoot = false;
7 hash = "sha256-xtlG3UQ/WClt/Hvxe+oId2CeJ+PWMDXBUJXh5+k+mZQ=";
8 };
9in
10{
11 flake.modules.nixos.media-server =
12 { pkgs, config, lib, ... }:
13 {
14 services.flaresolverr.enable = true;
15
16 services.immich = {
17 enable = true;
18 mediaLocation = "/mnt/storage2/immich";
19 host = "0.0.0.0";
20 openFirewall = true;
21 };
22
23 # Immich uses PostgreSQL via Unix socket; avoid conflicting with lbdt-postgres on TCP/5432.
24 services.postgresql.settings.listen_addresses = lib.mkForce "";
25
26 systemd.tmpfiles.rules = [
27 "d /mnt/storage2/immich 0750 immich immich -"
28
29 # Required before transmission.service starts because its sandbox bind-mounts
30 # these paths. nixarr declares them too, but quote-wrapped tmpfiles paths can
31 # fail to materialize before the service restart during switch.
32 "d /mnt/storage1/nixarr/media/torrents 0755 transmission media - -"
33 "d /mnt/storage1/nixarr/media/torrents/.incomplete 0755 transmission media - -"
34 "d /mnt/storage1/nixarr/media/torrents/.watch 0755 transmission media - -"
35 ];
36
37 # nixarr configures SABnzbd through services.sabnzbd.settings on newer nixpkgs,
38 # but NixOS keeps the legacy configFile default when system.stateVersion < 26.05,
39 # causing those settings to be ignored. Force the new settings-backed path.
40 services.sabnzbd.configFile = lib.mkForce null;
41
42 age.secrets.wireguard.file = ../secrets/wireguard.age;
43
44 nixarr = {
45 enable = true;
46 mediaDir = "/mnt/storage1/nixarr/media";
47 vpn = {
48 enable = true;
49 wgConf = config.age.secrets.wireguard.path;
50 };
51
52 jellyfin = {
53 enable = true;
54 openFirewall = true;
55 };
56
57 transmission = {
58 enable = true;
59 vpn.enable = true;
60 peerPort = 51413;
61 };
62 sabnzbd = {
63 enable = true;
64 vpn.enable = true;
65 openFirewall = true;
66 };
67
68 prowlarr.enable = true;
69 radarr.enable = true;
70 sonarr.enable = true;
71 seerr = {
72 enable = true;
73 openFirewall = true;
74 };
75
76 recyclarr = {
77 enable = true;
78 configuration = {
79 sonarr = {
80 series = {
81 base_url = "http://localhost:8989";
82 api_key = "!env_var SONARR_API_KEY";
83 quality_definition = {
84 type = "series";
85 };
86 delete_old_custom_formats = true;
87 custom_formats = [
88 {
89 trash_ids = [
90 "85c61753df5da1fb2aab6f2a47426b09"
91 "9c11cd3f07101cdba90a2d81cf0e56b4"
92 ];
93 assign_scores_to = [
94 {
95 name = "WEB-DL (1080p)";
96 score = -10000;
97 }
98 ];
99 }
100 ];
101 };
102 };
103 radarr = {
104 movies = {
105 base_url = "http://localhost:7878";
106 api_key = "!env_var RADARR_API_KEY";
107 quality_definition = {
108 type = "movie";
109 };
110 delete_old_custom_formats = true;
111 custom_formats = [
112 {
113 trash_ids = [
114 "570bc9ebecd92723d2d21500f4be314c"
115 "eca37840c13c6ef2dd0262b141a5482f"
116 ];
117 assign_scores_to = [
118 {
119 name = "HD Bluray + WEB";
120 score = 25;
121 }
122 ];
123 }
124 ];
125 };
126 };
127 };
128 };
129 };
130
131 # Avoid jellyfin blocking shutdown for 2 minutes
132 systemd.services.jellyfin.serviceConfig.TimeoutStopSec = 10;
133
134 # Install Kodi Sync Queue plugin into Jellyfin
135 systemd.services.jellyfin.serviceConfig.ExecStartPre =
136 let
137 pluginDir = "/data/.state/nixarr/jellyfin/data/plugins/Kodi Sync Queue/15.0.0.0";
138 in
139 pkgs.writeShellScript "install-jellyfin-plugins" ''
140 mkdir -p "${pluginDir}"
141 cp -f ${jellyfinKodiSyncQueue}/*.dll ${jellyfinKodiSyncQueue}/meta.json "${pluginDir}/"
142 '';
143 };
144}