added jellyfin
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
# Copy to .env and adjust. .env is git-ignored; this file is not.
|
||||
# Nothing here is secret, but keep it that way.
|
||||
|
||||
# ─── Identity ───────────────────────────────────────────────────────
|
||||
# These three are the same for every service on the machine, so compose.yaml
|
||||
# already defaults them to 1000/1000/Etc/UTC. Three places can set them, in
|
||||
# increasing priority:
|
||||
#
|
||||
# 1. the defaults in compose.yaml (a fresh clone just works)
|
||||
# 2. this file (per-service)
|
||||
# 3. exported in your shell (global, wins over this file)
|
||||
#
|
||||
# If you keep a global `export PUID=...` in ~/.profile, leave these commented.
|
||||
#
|
||||
# The host user that should own everything Jellyfin writes: `id -u` / `id -g`.
|
||||
# This is the setting that bites hardest when it's wrong: the container starts
|
||||
# fine and then can't write its database, or writes it as root and you can no
|
||||
# longer touch the files from the host.
|
||||
#PUID=1000
|
||||
#PGID=1000
|
||||
|
||||
# Affects timestamps in logs and scheduled-task times.
|
||||
#TZ=Europe/Paris
|
||||
|
||||
# ─── Paths ──────────────────────────────────────────────────────────
|
||||
# Jellyfin's own state: database, metadata, artwork, subtitles, logs.
|
||||
# Small enough to live next to the compose file — see the README. The
|
||||
# repo's .gitignore keeps it out of git either way. Defaults to ./config.
|
||||
#CONFIG_PATH=./config
|
||||
|
||||
# Your main media library. REQUIRED — compose refuses to start without it,
|
||||
# rather than silently mounting nothing.
|
||||
MEDIA_PATH=/srv/media
|
||||
|
||||
# Extra libraries on other disks. Uncomment the matching lines in
|
||||
# compose.yaml too, otherwise these do nothing.
|
||||
#MEDIA_PATH_2=/mnt/disk2/tv
|
||||
#MEDIA_PATH_3=/mnt/scratch/downloads
|
||||
|
||||
# ─── Ports ──────────────────────────────────────────────────────────
|
||||
# Defaulted in compose.yaml; set here only to move them.
|
||||
#PORT=8096
|
||||
#DISCOVERY_PORT=7359
|
||||
|
||||
# ─── Hardware transcoding ───────────────────────────────────────────
|
||||
# GID of the host group that owns /dev/dri/renderD128. Host-specific:
|
||||
#
|
||||
# getent group render | cut -d: -f3
|
||||
#
|
||||
# It varies wildly between distros and installs (105, 992, 993, 989 are
|
||||
# all real answers I've seen). Do not copy the number below — look it up.
|
||||
# REQUIRED while the hardware-transcoding block is present in compose.yaml;
|
||||
# delete that block and this line together if you transcode on the CPU.
|
||||
RENDER_GID=105
|
||||
@@ -0,0 +1,110 @@
|
||||
# Jellyfin
|
||||
|
||||
Media server. Reads films and TV off disk and streams them to whatever is in
|
||||
the room — no account, no subscription, nothing phoning home.
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
$EDITOR .env # MEDIA_PATH and RENDER_GID are required
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Open `http://<host>:8096` and point your libraries at `/media` — the path
|
||||
*inside* the container, whatever `MEDIA_PATH` is on the host.
|
||||
|
||||
## Ports
|
||||
|
||||
| Port | Proto | Purpose | |
|
||||
|------|-------|---------|---|
|
||||
| 8096 | http | Web UI and client API | published |
|
||||
| 7359 | udp | LAN auto-discovery | published |
|
||||
| 8920 | https | Jellyfin's own TLS listener | off — TLS belongs at the proxy |
|
||||
| 1900 | udp | DLNA / SSDP | off — uncomment if you use DLNA |
|
||||
|
||||
## Why it looks like this
|
||||
|
||||
**The LinuxServer image.** I started with it years ago, it has worked every day
|
||||
since, and I'm not changing a base image to fix nothing. The better reason: I
|
||||
run several of their images and they all speak the same dialect —
|
||||
`PUID`/`PGID`/`TZ`, `/config`, s6, one rebuild cadence. Learn one, learn the
|
||||
family. Jellyfin's official image is also fine; it just doesn't do the
|
||||
`PUID`/`PGID` dance, so ownership works differently.
|
||||
|
||||
**`/config` lives next to the compose file**, breaking this repo's own
|
||||
"data outside the repo" rule on purpose. It's Jellyfin's database, metadata and
|
||||
artwork — 988M after three years here. Under a gigabyte, and keeping it beside
|
||||
the compose file means the whole service is one `tar` from being moved.
|
||||
`.gitignore` keeps it out of git. Note that transcode scratch files land under
|
||||
`/config` and *can* spike several GB — move that path in **Dashboard → Playback**
|
||||
if the disk is small.
|
||||
|
||||
**`PUID`/`PGID`/`TZ` carry defaults** (`${PUID:-1000}`) because they're the same
|
||||
for every service on a box. Compose resolves them from the default, then `.env`,
|
||||
then the ambient environment — so a global `export PUID=…`, or OpenMediaVault's
|
||||
compose plugin, wins. That last one is why my own copy has bare `${PUID}`: OMV
|
||||
supplies those three, so they're never empty *on my machine*. Without OMV they
|
||||
would be, and Compose interpolates an empty string and starts anyway rather than
|
||||
failing. Hence the defaults.
|
||||
|
||||
Values that can't be guessed use `${VAR:?message}` instead and abort:
|
||||
|
||||
```console
|
||||
$ docker compose up -d
|
||||
error while interpolating services.jellyfin.group_add.[]: required variable
|
||||
RENDER_GID is missing a value: find yours with: getent group render | cut -d: -f3
|
||||
```
|
||||
|
||||
**One `/media` mount.** My real server has three libraries on separate disks;
|
||||
that's my risk appetite, not yours. Add a line per library if you need more.
|
||||
Upstream's example splits `/data/tvshows` and `/data/movies` — fine on separate
|
||||
disks, but if they share a filesystem, mounting the common parent is the better
|
||||
habit: hardlinks only survive within one mount point inside the container.
|
||||
Mount media `:ro` if you like — Jellyfin never writes to it.
|
||||
|
||||
**Hardware transcoding needs `devices` *and* `group_add`.** Passing
|
||||
`/dev/dri/renderD128` in is half the job; the node is owned by a host group and
|
||||
the container user has to be in it or the device opens and does nothing. The
|
||||
catch is that `group_add` wants a numeric GID and it differs per host — mine is
|
||||
`105`, on the laptop I wrote this on it's `992`. Copying someone's compose file
|
||||
verbatim is exactly how you get silently broken acceleration.
|
||||
|
||||
```bash
|
||||
getent group render | cut -d: -f3
|
||||
```
|
||||
|
||||
Not on Intel? Delete both keys — CPU transcoding works, it just costs cores.
|
||||
NVIDIA and AMD variants:
|
||||
[LinuxServer's hardware acceleration docs](https://docs.linuxserver.io/images/docker-jellyfin/#hardware-acceleration).
|
||||
|
||||
**`restart: unless-stopped`, not `always`** — if I stop it deliberately, it
|
||||
should stay stopped across a reboot.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **`:latest` is a choice.** Pin a tag if that makes you nervous. Back up
|
||||
`/config` either way — that directory *is* your server.
|
||||
- **Auto-discovery is LAN-only.** 7359/udp is broadcast; behind a proxy it does
|
||||
nothing for you. Set `JELLYFIN_PublishedServerUrl` there instead.
|
||||
- **On a subpath, set the base URL** under **Dashboard → Networking** first, or
|
||||
the UI loads and every asset 404s. A subdomain avoids the question.
|
||||
- **Verify hardware transcoding actually engaged.** Enabling VAAPI and it
|
||||
*working* are different states — force a transcode and check
|
||||
**Dashboard → Playback**. A wrong GID falls back to software silently.
|
||||
|
||||
## Exposing it
|
||||
|
||||
Publishes plain HTTP; nothing here is proxy-aware.
|
||||
|
||||
- [Caddy reverse proxy](../docs/reverse-proxy.md) — what I do.
|
||||
- [Cloudflare Tunnel](../docs/cloudflare-tunnel.md) — **not for this one.**
|
||||
Streaming video through a tunnel breaks Cloudflare's terms and performs badly.
|
||||
- [Authentik SSO](../docs/authentik-sso.md) — works for the web UI, but native
|
||||
clients can't handle a forward-auth login page.
|
||||
|
||||
## Links
|
||||
|
||||
- Jellyfin docs: <https://jellyfin.org/docs/>
|
||||
- Hardware acceleration: [upstream](https://jellyfin.org/docs/general/administration/hardware-acceleration/) · [this image](https://docs.linuxserver.io/images/docker-jellyfin/#hardware-acceleration)
|
||||
- Image: <https://docs.linuxserver.io/images/docker-jellyfin/>
|
||||
@@ -0,0 +1,44 @@
|
||||
services:
|
||||
jellyfin:
|
||||
image: lscr.io/linuxserver/jellyfin:latest
|
||||
container_name: jellyfin
|
||||
restart: unless-stopped
|
||||
|
||||
# Defaults make a fresh clone work with no .env at all. Anything you set
|
||||
# -- in this service's .env, or exported globally in your shell -- wins.
|
||||
environment:
|
||||
- PUID=${PUID:-1000}
|
||||
- PGID=${PGID:-1000}
|
||||
- TZ=${TZ:-Etc/UTC}
|
||||
# The address Jellyfin advertises to clients during auto-discovery. Worth
|
||||
# setting behind a reverse proxy, where clients otherwise get handed the
|
||||
# container's internal address and fail to connect.
|
||||
#- JELLYFIN_PublishedServerUrl=https://jellyfin.example.com
|
||||
|
||||
volumes:
|
||||
# Jellyfin's own state. Stays next to this file on purpose — see README.
|
||||
- ${CONFIG_PATH:-./config}:/config
|
||||
- "${MEDIA_PATH:?set MEDIA_PATH in .env to your media library}:/media"
|
||||
# Extra libraries on other disks. One line per library; the path after
|
||||
# the colon is what you browse to when adding the library in the UI.
|
||||
#- ${MEDIA_PATH_2}:/media2
|
||||
#- ${MEDIA_PATH_3}:/media3
|
||||
|
||||
ports:
|
||||
- "${PORT:-8096}:8096"
|
||||
# LAN client auto-discovery. Drop it if you always reach Jellyfin by
|
||||
# name or through a reverse proxy.
|
||||
- "${DISCOVERY_PORT:-7359}:7359/udp"
|
||||
# Jellyfin's own HTTPS listener. Off here: TLS terminates at the proxy.
|
||||
#- "8920:8920"
|
||||
# DLNA / SSDP. Only for devices that discover servers that way.
|
||||
#- "1900:1900/udp"
|
||||
|
||||
# ─── Hardware transcoding: Intel Quick Sync / VAAPI ──────────────────
|
||||
# Delete both keys below if you transcode on the CPU, or replace them
|
||||
# with the NVIDIA/AMD equivalent. RENDER_GID is host-specific — the
|
||||
# .env.example explains how to find yours.
|
||||
devices:
|
||||
- /dev/dri/renderD128:/dev/dri/renderD128
|
||||
group_add:
|
||||
- "${RENDER_GID:?find yours with: getent group render | cut -d: -f3}"
|
||||
@@ -1,22 +0,0 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
|
||||
jellyfin:
|
||||
image: linuxserver/jellyfin
|
||||
restart: unless-stopped
|
||||
#hostname: "${DEVICE_HOSTNAME}"
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=Europe/Paris
|
||||
volumes:
|
||||
- ./config:/config
|
||||
- ../media:/media
|
||||
# - /media/umbrl/GPass_Serie:/hard_Drive
|
||||
# - /media/umbrl/Videos:/hdd_movie_all
|
||||
ports:
|
||||
# Service auto-discovery
|
||||
- 7359:7359/udp
|
||||
- 8096:8096
|
||||
|
||||
Reference in New Issue
Block a user