{ config, pkgs, ... }: let cids = import ./staticids.nix; networkPrefix = config.networkPrefix; pyloadUser = { isSystemUser = true; uid = cids.uids.pyload; group = "pyload"; home = "/var/lib/pyload"; createHome = true; }; pyloadGroup = { gid = cids.gids.pyload; }; jellyfinUser = { isSystemUser = true; uid = cids.uids.jellyfin; group = "jellyfin"; home = "/var/lib/jellyfin"; createHome = true; extraGroups = [ "render" "video" ]; }; jellyfinGroup = { gid = cids.gids.jellyfin; }; in { users.users.pyload = pyloadUser; users.groups.pyload = pyloadGroup; users.users.jellyfin = jellyfinUser; users.groups.jellyfin = jellyfinGroup; # Create the multimedia directory structure on the host systemd.tmpfiles.rules = [ "d /var/lib/multimedia 0755 root root - -" "d /var/lib/multimedia/downloads 0755 pyload pyload - -" "d /var/lib/multimedia/movies 0755 jellyfin jellyfin - -" "d /var/lib/multimedia/tv-shows 0755 jellyfin jellyfin - -" "d /var/lib/multimedia/music 0755 jellyfin jellyfin - -" "d /var/lib/jellyfin 0755 jellyfin jellyfin - -" ]; containers.pyload = { autoStart = true; ephemeral = false; privateNetwork = true; hostBridge = "server"; hostAddress = "${networkPrefix}.97.1"; localAddress = "${networkPrefix}.97.11/24"; # GPU device passthrough for hardware transcoding allowedDevices = [ { modifier = "rwm"; node = "/dev/dri/card0"; } { modifier = "rwm"; node = "/dev/dri/renderD128"; } ]; bindMounts = { "/dev/dri" = { hostPath = "/dev/dri"; isReadOnly = false; }; "/run/opengl-driver" = { hostPath = "/run/opengl-driver"; isReadOnly = true; }; "/nix/store" = { hostPath = "/nix/store"; isReadOnly = true; }; "/var/lib/pyload" = { hostPath = "/var/lib/pyload"; isReadOnly = false; }; "/var/lib/jellyfin" = { hostPath = "/var/lib/jellyfin"; isReadOnly = false; }; "/multimedia" = { hostPath = "/var/lib/multimedia"; isReadOnly = false; }; }; config = { lib, config, pkgs, ... }: { nixpkgs.overlays = [ (import ../utils/overlays/packages.nix) ]; nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "unrar" ]; environment.systemPackages = with pkgs; [ unrar # Required for RAR archive extraction ]; # Intel graphics support for hardware transcoding hardware.graphics = { enable = true; extraPackages = with pkgs; [ intel-media-driver vpl-gpu-rt intel-compute-runtime ]; }; # Set VA-API driver to iHD (modern Intel driver for N100) environment.sessionVariables = { LIBVA_DRIVER_NAME = "iHD"; }; networking = { hostName = "pyload"; useHostResolvConf = false; defaultGateway = { address = "${networkPrefix}.97.1"; interface = "eth0"; }; nameservers = [ "${networkPrefix}.97.1" ]; firewall.enable = false; }; services.pyload = { enable = true; downloadDirectory = "/multimedia/downloads"; listenAddress = "0.0.0.0"; port = 8000; }; services.jellyfin = { enable = true; openFirewall = true; }; # Override systemd hardening for GPU access systemd.services.jellyfin = { serviceConfig = { PrivateUsers = lib.mkForce false; # Disable user namespacing - breaks GPU device access DeviceAllow = [ "/dev/dri/card0 rw" "/dev/dri/renderD128 rw" ]; SupplementaryGroups = [ "render" "video" ]; # Critical: Explicit group membership for GPU access }; environment = { LIBVA_DRIVER_NAME = "iHD"; # Ensure service sees this variable }; }; # Disable SSL certificate verification systemd.services.pyload = { environment = { PYLOAD__GENERAL__SSL_VERIFY = "0"; }; # Bind-mount DNS configuration files and system tools into the chroot serviceConfig = { BindReadOnlyPaths = [ "/etc/resolv.conf" "/etc/nsswitch.conf" "/etc/hosts" "/etc/ssl" "/etc/static/ssl" # Make all system packages (including unrar) accessible "/run/current-system/sw/bin" ]; }; }; # Ensure render/video groups exist with consistent GIDs for GPU access users.groups.render = { gid = 303; }; users.groups.video = { gid = 26; }; users.users.pyload = pyloadUser; users.groups.pyload = pyloadGroup; users.users.jellyfin = jellyfinUser; users.groups.jellyfin = jellyfinGroup; system.stateVersion = "24.05"; }; }; }