{ config, lib, pkgs, ... }: let cfg = config.homelab.apps.jellyfin; networkName = "jellyfin"; UID = 3008; GID = config.users.groups.media.gid; in { options.homelab.apps.jellyfin.enable = lib.mkEnableOption "Jellyfin using Docker"; config = lib.mkIf cfg.enable { homelab = { users = { apps.enable = true; media.enable = true; }; virtualisation.containers.enable = true; }; fileSystems = { "/srv/audio" = { device = "192.168.0.11:/mnt/SMALL/MEDIA/AUDIO"; fsType = "nfs"; options = [ "ro" "nfsvers=4.2" "async" "soft" "timeo=100" "retry=50" "actimeo=1800" "lookupcache=all" "nosuid" "tcp" ]; }; "/srv/video" = { device = "192.168.0.11:/mnt/SMALL/MEDIA/VIDEO"; fsType = "nfs"; options = [ "ro" "nfsvers=4.2" "async" "soft" "timeo=100" "retry=50" "actimeo=1800" "lookupcache=all" "nosuid" "tcp" ]; }; "/srv/homevideo" = { device = "192.168.0.11:/mnt/BIG/MEDIA/HOMEVIDEO/ARCHIVE"; fsType = "nfs"; options = [ "ro" "nfsvers=4.2" "async" "soft" "timeo=100" "retry=50" "actimeo=1800" "lookupcache=all" "nosuid" "tcp" ]; }; "/srv/photo" = { device = "192.168.0.11:/mnt/BIG/MEDIA/PHOTO/ARCHIVE"; fsType = "nfs"; options = [ "ro" "nfsvers=4.2" "async" "soft" "timeo=100" "retry=50" "actimeo=1800" "lookupcache=all" "nosuid" "tcp" ]; }; }; users.users.jellyfin = { uid = lib.mkForce UID; isSystemUser = true; group = config.users.groups.apps.name; extraGroups = [ config.users.groups.media.name ]; home = "/var/empty"; shell = null; }; # Make sure the Docker network exists. systemd.services."docker-${networkName}-create-network" = { description = "Create Docker network for ${networkName}"; requiredBy = [ "docker-jellyfin.service" "docker-feishin.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' if ! ${pkgs.docker}/bin/docker network ls | grep -q ${networkName}; then ${pkgs.docker}/bin/docker network create ${networkName} fi ''; }; virtualisation.oci-containers.containers = { jellyfin = { hostname = "jellyfin"; image = "jellyfin/jellyfin:10.10.0"; user = "${toString UID}:${toString GID}"; autoStart = true; ports = [ "8096:8096/tcp" # "8920:8920/tcp" ]; extraOptions = [ "--network=${networkName}" "--device=nvidia.com/gpu=all" # Equivalent to --gpus=all ]; volumes = [ "jellyfin-config:/config" "cache:/cache" "/srv/audio:/media/audio" "/srv/video:/media/video" "/srv/homevideo:/media/homevideo" "/srv/photo:/media/photo" ]; environment = { # TODO }; }; jellyfin-vue = { hostname = "jellyfin-vue"; image = "ghcr.io/jellyfin/jellyfin-vue:unstable"; autoStart = true; ports = [ "8080:80/tcp" ]; extraOptions = [ "--network=${networkName}" ]; labels = { }; }; feishin = let feishinPort = "9180"; in { hostname = "feishin"; image = "ghcr.io/jeffvli/feishin:0.7.1"; autoStart = true; ports = [ "${feishinPort}:9180/tcp" # Web player (HTTP) ]; extraOptions = [ "--network=${networkName}" ]; environment = { # pre defined server name SERVER_NAME = "Hugo"; # When true AND name/type/url are set, only username/password can be toggled SERVER_LOCK = "true"; # Either "jellyfin" or "navidrome" SERVER_TYPE = "jellyfin"; # http://address:port SERVER_URL= "https://jelly.depeuter.dev"; TZ = config.time.timeZone; }; labels = { "traefik.enable" = "true"; "traefik.http.routers.feishin.rule" = "Host(`play.jelly.depeuter.dev`)"; "traefik.http.services.feishin.loadbalancer.server.port" = feishinPort; "traefik.tls.options.default.minVersion" = "VersionTLS13"; }; }; }; }; }