Files
nixos/services/media/default.nix
T
Hermes Agent 667a072937 feat(media): migrer la bibliothèque partagée vers /srv/media (dataset ZFS dédié)
/home/marc/media posait deux problèmes : /home/marc est 0700 (chaque service
devait recevoir un droit de traversée) et systemd-tmpfiles refuse de créer
quoi que ce soit sous /home/marc (« unsafe path transition », exit 73).

- services/media (nouveau) : dataset nixos/media -> /srv/media, groupe media
  (setgid, dossiers 2775/2770), arborescence movies/tvshows/music/books/downloads
- disko.nix : dataset media à côté de marc/nix
- navidrome, jellyfin, kodi : bibliothèque -> /srv/media/{music,movies,tvshows}
- qbittorrent : téléchargements dans le même dataset que la bibliothèque
  (permet aux *arr d'importer par lien dur au lieu de copier)
- radarr : dataDir corrigé (pointait sur celui de sonarr par copier-coller),
  déplacé hors /home (impossible avec ProtectHome+tmpfiles du module)
- sonarr : dataDir dédié + migration depuis /home/marc/radarr (était figé
  depuis le 28/07, port 8006 muet) ; réparé le 27/09/2026
- readarr : bibliothèque -> /srv/media/books
2026-09-27 21:19:52 -04:00

80 lines
3.7 KiB
Nix

{
config,
lib,
pkgs,
...
}:
# Média partagé : /srv/media (dataset ZFS nixos/media) + groupe `media`.
#
# Pourquoi pas /home/marc/media (ancien emplacement) :
# - /home/marc est en 0700, donc chaque service devait recevoir un droit de
# traversée explicite (ajout au groupe `users`) pour lire /home/marc/media ;
# - systemd-tmpfiles REFUSE de créer quoi que ce soit sous /home/marc
# (« Detected unsafe path transition », exit 73) : les règles ne
# s'appliquaient jamais et il fallait un oneshot root (cf. services/jellyfin).
# Hors /home, les deux problèmes disparaissent : les dossiers sont déclaratifs et
# les services n'ont plus besoin de traverser le home de marc.
#
# Modèle de droits : un seul groupe `media`, dossiers en 2775 (setgid, donc les
# fichiers créés héritent du groupe `media`) et UMask=0002 posé sur chaque
# service qui écrit (cf. les modules concernés) pour que ses fichiers restent
# modifiables par les autres membres. Nextcloud écrit en 0644 (son umask), donc
# lisible par Jellyfin mais pas modifiable par les *arr : suffisant, ils ne
# réécrivent pas ses fichiers.
let
mediaRoot = "/srv/media";
in {
users.groups.media = { };
users.users.marc.extraGroups = [ "media" ];
users.users.jellyfin.extraGroups = [ "media" ];
users.users.navidrome.extraGroups = [ "media" ];
users.users.qbittorrent.extraGroups = [ "media" ];
users.users.nextcloud.extraGroups = [ "media" ];
# Création du dataset, une seule fois : l'activation tourne en root, et ZFS
# monte ensuite le dataset tout seul à chaque boot (propriété mountpoint).
# Idempotent : ne recrée rien si nixos/media existe déjà.
systemd.services.media-dataset = {
description = "Création du dataset ZFS du média partagé (nixos/media -> ${mediaRoot})";
wantedBy = [ "multi-user.target" ];
unitConfig.ConditionPathExists = "!${mediaRoot}";
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
ZFS=${config.boot.zfs.package}/bin/zfs
if ! $ZFS list -H -o name nixos/media >/dev/null 2>&1; then
$ZFS create -o mountpoint=${mediaRoot} nixos/media
fi
$ZFS set mountpoint=${mediaRoot} nixos/media || true
${pkgs.util-linux}/bin/mountpoint -q ${mediaRoot} || $ZFS mount nixos/media || true
# L'arbre est aussi créé ici : au premier démarrage, systemd-tmpfiles
# tourne avant le montage du dataset et ses dossiers seraient masqués.
${pkgs.coreutils}/bin/install -d -o root -g media -m 2755 ${mediaRoot}
for d in movies tvshows music books; do
${pkgs.coreutils}/bin/install -d -o root -g media -m 2775 "${mediaRoot}/$d"
done
${pkgs.coreutils}/bin/install -d -o root -g media -m 2770 ${mediaRoot}/downloads
for d in radarr sonarr readarr incomplete; do
${pkgs.coreutils}/bin/install -d -o root -g media -m 2770 "${mediaRoot}/downloads/$d"
done
'';
};
# Bibliothèque (lue par Jellyfin, Navidrome et Kodi, écrite par les *arr) et
# zone de téléchargement (clients + imports). Les deux sont dans le MÊME
# dataset : c'est ce qui permet aux *arr d'importer par lien dur au lieu de
# copier, et donc de laisser qBittorrent seeder sans doubler l'espace.
# `books` par symétrie avec Readarr. Les téléchargements sont en 2770 : les
# torrents en cours n'ont pas besoin d'être lisibles par tout le monde.
systemd.tmpfiles.rules =
[ "d ${mediaRoot} 2755 root media -" ]
++ map (d: "d ${mediaRoot}/${d} 2775 root media -") [ "movies" "tvshows" "music" "books" ]
++ [ "d ${mediaRoot}/downloads 2770 root media -" ]
++ map (d: "d ${mediaRoot}/downloads/${d} 2770 root media -") [ "radarr" "sonarr" "readarr" "incomplete" ];
}