New releases
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Editorial pickMedia servers
Jellyfin turns a home server into a media library: it finds your files, pulls in artwork and descriptions, remembers where you stopped, and streams to a TV, a phone, and the browser. Fully free software — no subscription, no account, no mandatory cloud.
Jellyfin is a media server for a home collection of movies, series, music, and photos. It scans your folders, fetches artwork, descriptions, and cast lists, remembers where you stopped, and streams video to the browser, a phone, a tablet, a TV, and a set-top box. It suits anyone who keeps a library locally and does not want to depend on subscriptions or on a service staying available.
The project is a fork of Emby made after it moved to a closed license, and it stays entirely free software: no paid features, no subscription, no vendor account. Clients exist for Android, iOS, Android TV, Kodi, and desktop systems, and the web interface works in any browser.
The recipe runs a single container. Settings, the database, and metadata live in
a Docker volume, the cache in a second volume, and the media directory is mounted
read-only. The reliable level is not claimed: Jellyfin uses embedded SQLite,
and the recipe adds no monitoring and no resource limits.
Transcoding is the heaviest thing the server does. If clients play files directly, a weak CPU is enough; if they cannot, you need a fast CPU or hardware acceleration, which this recipe does not configure.
The container runs without privileged, host networking, or Docker socket
access, and uses no-new-privileges; only the web port is published, and only on
127.0.0.1. The media library is mounted read-only, so the server cannot modify
or delete your files. DLNA and autodiscovery, which need host networking, are
deliberately disabled.
The main risk is the first start: the setup wizard requires no password, and the
port must not be exposed until the administrator account exists. The container
runs as root, as in the upstream instructions; if that matters, set user: and
adjust volume ownership yourself. Publish the server over HTTPS only, enforce
strong passwords, and keep the number of external users small: a single 4K
transcode can saturate the CPU.
The recipe image is scanned with Trivy; the critical CVEs it found are listed on the card. As with any large media server, these are mostly base image system library issues that upstream closes with the next release.
These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Result of a dedicated Docker image scan, not a Compose configuration check.
A new major version is found by comparing version numbers. A compatibility verdict is entered by hand after reading the release notes and the migration steps.
Upstream Compose changes relative to the verified recipe.
Pinned image tags checked for newer versions.
From observation to action
The recipe already pins the latest known release. The steps below show the evidence retained for that version.
v10.11.11v10.11.11Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.
The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.
No image-tag change is currently planned.
Migration, majors cannot be skipped. Release-specific migration steps still come from the release notes and installation guide.
No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.
An exact Fully verified check for this target was recorded on 2026-08-30.
The status shows how fully the published recipe meets each level's requirements.
A minimal setup for a local network or access through a VPN.
The recipe covers every requirement at this level.
A public deployment with protected access and backups.
The recipe covers every requirement at this level.
A setup designed for predictable operations and observability.
You still need to add: separate database, external monitoring, cpu and memory limits.
Tailored to your server
Answer five questions and download a ready-to-run Jellyfin bundle.
Screenshots have not been added yet.
Read the full guide first: it explains external access, backups, and data locations.
cp .env.example .envdocker compose up -ddocker compose psFor this recipe budget at least 2 CPU cores, 1 GB of RAM, and 5 GB of disk for the server itself; 4 GB of RAM is recommended. This is a conservative estimate for the recipe: upstream publishes no formal minimum, because the load depends entirely on how you use it. Direct play costs almost nothing, while transcoding a single 4K stream can saturate the CPU. Count storage for the media library separately. You need Ubuntu 22.04+ or Debian 12+ with Docker Engine and Docker Compose v2.24+.
docker --version
docker compose version
nproc && free -m
Artwork and descriptions are fetched automatically only when files follow the upstream naming rules: one directory per movie, with the year in parentheses.
/srv/media/Movies/Sintel (2010)/Sintel (2010).mkv
/srv/media/Movies/Tears of Steel (2012)/Tears of Steel (2012).mkv
/srv/media/Shows/Show Name (2021)/Season 01/Show Name S01E01.mkv
Put the recipe files in their own directory and create a private .env:
mkdir -p ~/services/jellyfin
cd ~/services/jellyfin
cp .env.example .env
chmod 600 .env
sed -i "s|^JELLYFIN_MEDIA_LOCATION=.*|JELLYFIN_MEDIA_LOCATION=/srv/media|" .env
JELLYFIN_VERSION pins the image; JELLYFIN_PORT sets the local port;
JELLYFIN_PUBLISHED_URL is the external address the server announces to
clients; JELLYFIN_MEDIA_LOCATION is the media directory, mounted read-only;
JELLYFIN_CONFIG_VOLUME and JELLYFIN_CACHE_VOLUME name the Docker volumes;
TZ sets the time zone.
Settings, the database, metadata, and accounts live in the /config volume.
/cache holds thumbnails and transcoding temporaries — that volume is rebuilt
by a rescan and does not belong in a backup.
The wizard is not password protected: until an administrator exists, whoever opens the port owns the server. Run the first start over localhost or an SSH tunnel.
docker compose pull
docker compose up -d
docker compose ps
ssh -L 8096:127.0.0.1:8096 user@server.example
Open http://localhost:8096, choose the interface language, create the
administrator, and add a library pointing at /media/Movies — that is the path
inside the container, not on the host. After the scan finishes, check that
artwork was fetched.
On a VPS keep the 127.0.0.1 bind, block port 8096 from outside, and publish the
service only through an HTTPS reverse proxy. Check the state and the API:
docker compose up -d
docker compose ps
curl --fail http://127.0.0.1:8096/health
Keep in mind that a shared vCPU is almost always too weak for transcoding. Store files in formats your clients play directly, and watch Dashboard → Playback to see when the server starts transcoding.
Without TLS use an SSH tunnel or a VPN. If the reverse proxy runs on another host
in a trusted LAN, replace 127.0.0.1 in compose.yaml with the server’s LAN
address and restrict the port to the proxy address in the firewall.
Client autodiscovery uses UDP port 7359: to enable it, add - "7359:7359/udp" to
the ports section. DLNA requires network_mode: host and is deliberately not
enabled in this recipe — it gives up the container’s network isolation, so turn
it on only inside a trusted network.
Replace media.example.com with your single domain in the file you pick from
proxy/, and set the same address in JELLYFIN_PUBLISHED_URL.
proxy/Caddyfile obtains a certificate automatically; proxy/nginx.conf expects
a Certbot certificate; proxy/traefik.yaml uses the letsencrypt resolver. For
Traefik in a container, replace 127.0.0.1 with a host gateway it can reach.
All samples disable response buffering and pass WebSocket connections through: without that, seeking breaks and clients lose contact with the server. To see real client addresses in the activity log, add the proxy address under Dashboard → Networking → Known proxies.
curl --fail https://media.example.com/health
chmod +x backup.sh restore.sh
./backup.sh
The script stops the container, archives the whole /config volume, and starts
it again. Stopping is required: SQLite databases copy consistently only from a
stopped server. The archive holds settings, users, metadata, images, and watch
history.
The media library is not in the archive: those are your own files and need their own copy. The cache is not saved either — a rescan rebuilds it.
Jellyfin 10.11 also has its own mechanism: Dashboard → Backups → Create Backup
writes an archive to /config/data/backups without stopping the server. It is
handy before an update, and it ends up inside the copy that backup.sh makes.
Restoring replaces the contents of /config with the selected archive:
./restore.sh ./backups/jellyfin-YYYYMMDDTHHMMSSZ.tar.gz
docker compose ps
curl --fail http://127.0.0.1:8096/health
Before replacing anything the script takes a safety copy of the current data and
clears the cache: thumbnails and transcodes from the previous database become
invalid after the swap. The media library is untouched, but the library paths in
the restored database must match the current /media mount.
Take a backup and read the release notes. Change only the pinned
JELLYFIN_VERSION, then run:
./backup.sh
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 jellyfin
Upgrade one version at a time and do not skip major releases: database migrations assume an upgrade from the previous version.
Jellyfin has no downgrade mechanism. Migrations are applied the first time a new
image starts, after which the old version can no longer open the data. The only
way back is to restore the pinned JELLYFIN_VERSION in .env and restore the
archive taken before the update:
docker compose pull
./restore.sh ./backups/jellyfin-before-update.tar.gz
docker compose up -d
Watch history and metadata edits made after the update are not in that archive.
docker compose down removes the container but keeps the settings. Complete,
irreversible removal after verifying a backup:
docker compose down
docker volume rm jellyfin-config jellyfin-cache
rm -rf ~/services/jellyfin
The JELLYFIN_MEDIA_LOCATION directory stays untouched: the recipe mounts it
read-only and never deletes from it.
Sources: container installation, quick start, movie naming, backup and restore, and reverse proxy.
docker compose ps
docker compose logs --tail=200 jellyfin
docker inspect --format '{{json .State.Health}}' "$(docker compose ps -q jellyfin)"
The image healthcheck calls http://localhost:8096/health inside the container.
The usual causes are permissions on the /config volume or a full disk.
Check the path: the library must point at the path inside the container
(/media/Movies), not the host path (/srv/media/Movies).
docker compose exec jellyfin ls -la /media
If the directory is visible but nothing is found, it is the naming. Jellyfin
expects one directory per movie with the year in parentheses; files like
film1.mkv dumped in one folder are not recognized.
Metadata comes from external providers, so the server needs internet access. Check connectivity and refresh manually:
docker compose exec jellyfin curl -sI https://api.themoviedb.org | head -1
Then use Dashboard → Libraries → Scan All Libraries. For a single movie, the “Identify” action with a manual year or database link usually fixes it.
Open Dashboard → Playback and check whether transcoding is running. Direct play
barely loads the server; a 4K transcode saturates the CPU. Hardware acceleration
is not configured in this recipe; to enable it, pass the device through in
compose.yaml:
devices:
- /dev/dri:/dev/dri
and pick the method under Dashboard → Playback → Transcoding. Make sure the user inside the container can access the device.
Disable response buffering and allow WebSocket upgrades. Nginx needs
proxy_buffering off and the Upgrade/Connection headers; Caddy and Traefik
upgrade automatically. All samples in proxy/ are already configured.
That is the reverse proxy address. Add it under Dashboard → Networking → Known
proxies and the server will start trusting the X-Forwarded-For header.
Autodiscovery uses UDP port 7359, which this recipe does not publish. Add
- "7359:7359/udp" to ports, or enter the server address in the client by
hand. DLNA requires host networking and is deliberately disabled here.
docker system df -v | grep jellyfin
docker compose exec jellyfin du -sh /cache/* /config/metadata
The thumbnail and transcode cache can be deleted entirely; it will be rebuilt.
Metadata under /config/metadata grows with the library — deleting it means
downloading all the artwork again.
Look at the migration logs:
docker compose logs --tail=200 jellyfin | grep -i "migration\|error"
Jellyfin does not support downgrades: the only way back is restoring the archive taken before the update.
Fossary is an independent informational catalog and is not affiliated with the developers of the listed applications. We collect information from public sources and verify published recipes, but we do not develop, distribute, or control these applications.
A listing or validation status is not a requirement, endorsement, or personal recommendation to use an application. You decide whether it is suitable and use it at your own risk. We do not warrant an application's security, availability, or fitness for purpose and, to the extent permitted by law, are not liable for resulting loss or damage. Review its license, security settings, data processing, and backup procedures before installation.