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
30 # transmission.service bind-mounts these paths before ExecStartPre runs, while
31 # systemd-tmpfiles refuses to create them because /mnt/storage1 is user-owned.
32 # Create them in a small unsandboxed dependency before Transmission starts.
33 systemd.services.transmission-media-dirs = {
34 description = "Create Transmission media directories";
35 before = [ "transmission.service" ];
36 requiredBy = [ "transmission.service" ];
37 serviceConfig = {
38 Type = "oneshot";
39 RemainAfterExit = true;
40 };
41 script = ''
42 install -d -o transmission -g media -m 0755 \
43 /mnt/storage1/nixarr/media/torrents \
44 /mnt/storage1/nixarr/media/torrents/.incomplete \
45 /mnt/storage1/nixarr/media/torrents/.watch
46 '';
47 };
48 systemd.services.transmission = {
49 requires = [ "transmission-media-dirs.service" ];
50 after = [ "transmission-media-dirs.service" ];
51 };
52
53 # nixarr configures SABnzbd through services.sabnzbd.settings on newer nixpkgs,
54 # but NixOS keeps the legacy configFile default when system.stateVersion < 26.05,
55 # causing those settings to be ignored. Force the new settings-backed path.
56 services.sabnzbd.configFile = lib.mkForce null;
57
58 age.secrets.wireguard.file = ../secrets/wireguard.age;
59
60 nixarr = {
61 enable = true;
62 mediaDir = "/mnt/storage1/nixarr/media";
63 vpn = {
64 enable = true;
65 wgConf = config.age.secrets.wireguard.path;
66 };
67
68 jellyfin = {
69 enable = true;
70 openFirewall = true;
71 };
72
73 transmission = {
74 enable = true;
75 vpn.enable = true;
76 peerPort = 51413;
77 };
78 sabnzbd = {
79 enable = true;
80 vpn.enable = true;
81 openFirewall = true;
82 };
83
84 prowlarr.enable = true;
85 radarr.enable = true;
86 sonarr.enable = true;
87 seerr = {
88 enable = true;
89 openFirewall = true;
90 };
91
92 recyclarr = {
93 enable = true;
94 configuration = {
95 sonarr = {
96 series = {
97 base_url = "http://localhost:8989";
98 api_key = "!env_var SONARR_API_KEY";
99 quality_definition = {
100 type = "series";
101 };
102 delete_old_custom_formats = true;
103 custom_formats = [
104 {
105 trash_ids = [
106 "85c61753df5da1fb2aab6f2a47426b09"
107 "9c11cd3f07101cdba90a2d81cf0e56b4"
108 ];
109 assign_scores_to = [
110 {
111 name = "WEB-DL (1080p)";
112 score = -10000;
113 }
114 ];
115 }
116 ];
117 };
118 };
119 radarr = {
120 movies = {
121 base_url = "http://localhost:7878";
122 api_key = "!env_var RADARR_API_KEY";
123 quality_definition = {
124 type = "movie";
125 };
126 delete_old_custom_formats = true;
127 custom_formats = [
128 {
129 trash_ids = [
130 "570bc9ebecd92723d2d21500f4be314c"
131 "eca37840c13c6ef2dd0262b141a5482f"
132 ];
133 assign_scores_to = [
134 {
135 name = "HD Bluray + WEB";
136 score = 25;
137 }
138 ];
139 }
140 ];
141 };
142 };
143 };
144 };
145 };
146
147 # Avoid jellyfin blocking shutdown for 2 minutes
148 systemd.services.jellyfin.serviceConfig.TimeoutStopSec = 10;
149
150 # Install Kodi Sync Queue plugin into Jellyfin
151 systemd.services.jellyfin.serviceConfig.ExecStartPre =
152 let
153 pluginDir = "/data/.state/nixarr/jellyfin/data/plugins/Kodi Sync Queue/15.0.0.0";
154 in
155 pkgs.writeShellScript "install-jellyfin-plugins" ''
156 mkdir -p "${pluginDir}"
157 cp -f ${jellyfinKodiSyncQueue}/*.dll ${jellyfinKodiSyncQueue}/meta.json "${pluginDir}/"
158 '';
159 };
160}