diff --git a/README.md b/README.md index 0a4ed3c..efc9061 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,60 @@ # jellyfin-stack -Jellyfin LAN-only deploy on nullstone (tv.s8n.ru) \ No newline at end of file +Self-hosted Jellyfin media server on nullstone, LAN-only. + +## Endpoint + +- `https://tv.s8n.ru` — accessible only from LAN (192.168.0.0/24) and Tailscale admin/infra tags via Traefik `no-guest@file` middleware. +- DNS resolved internally by Pi-hole (`/opt/docker/pihole/etc-pihole/custom.list`). +- TLS via Let's Encrypt DNS-01 (Gandi). + +## Storage + +| Path | Purpose | +|-----------------------------------|-------------------------------| +| `/home/docker/jellyfin/config/` | Jellyfin config + DB (writable, UID 1000) | +| `/home/docker/jellyfin/cache/` | Transcode + image cache | +| `/home/user/media/movies/` | Movies library (mounted RO) | +| `/home/user/media/tv/` | TV library (mounted RO) | + +## Routing + +Traefik docker-label provider does NOT pick up the labels on this container +(unknown reason — file-provider routing for the same backend works). The +deploy uses **file-provider** routing in +`/opt/docker/traefik/config/jellyfin-test.yml`. If you fix the docker-provider +issue later, flip routing back to labels and remove the file-provider snippet. + +## Transcoding + +GTX 1660 Ti is present on nullstone but `nvidia-smi` currently fails — driver +is broken or not loaded. Jellyfin runs CPU-only transcode for now. After +fixing the driver, add the standard NVIDIA hwaccel block in compose: + +```yaml +deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] +``` + +…and enable NVENC in Jellyfin's Playback → Transcoding settings. + +## First-run setup + +1. Browse to `https://tv.s8n.ru` from the LAN. +2. Create the admin user (Jellyfin onboarding wizard). +3. Add libraries pointing at `/media/movies` and `/media/tv` inside the + container (these map to `/home/user/media/{movies,tv}`). +4. (Optional) Apply Netflix-style theme by pasting a community theme into + Dashboard → General → Custom CSS. + +## Deploy + +```bash +cd /opt/docker/jellyfin +docker compose up -d +``` diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b60e85e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,40 @@ +# Jellyfin — self-hosted media server (LAN-only) +# Deploy path on nullstone: /opt/docker/jellyfin/ +# Domain: tv.s8n.ru (LAN-only via Pi-hole local DNS + no-guest middleware) +# +# Notes: +# - GTX 1660 Ti present but nvidia-smi failing on host. CPU transcode only +# until driver is fixed; revisit hwaccel after fix. +# - Media mounted read-only into container; write only to /config + /cache. +# - userns: host matches nullstone Docker convention (host UID 1000 owns volumes). +# - Cert via existing letsencrypt resolver (Gandi DNS-01) — works without +# public A record. + +services: + jellyfin: + image: jellyfin/jellyfin:10.10.3 + container_name: jellyfin + restart: unless-stopped + user: "1000:1000" + environment: + - TZ=Europe/London + - JELLYFIN_PublishedServerUrl=https://tv.s8n.ru + volumes: + - /home/docker/jellyfin/config:/config + - /home/docker/jellyfin/cache:/cache + - /home/user/media:/media:ro + networks: + - proxy + labels: + - "traefik.enable=true" + - "traefik.docker.network=proxy" + - "traefik.http.routers.jellyfin.rule=Host(`tv.s8n.ru`)" + - "traefik.http.routers.jellyfin.entrypoints=websecure" + - "traefik.http.routers.jellyfin.tls=true" + - "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt" + - "traefik.http.routers.jellyfin.middlewares=security-headers@file,no-guest@file" + - "traefik.http.services.jellyfin.loadbalancer.server.port=8096" + +networks: + proxy: + external: true