Fully verified56,375

Editorial pickMedia servers

Jellyfin

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.

Alternative to PlexAlternative to EmbyGPL-2.0-onlyVersion v10.11.11

Overview

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.

Security assessment

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.

Update tracking

These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.

New releases

Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.

Current version v10.11.11Aug 30, 2026, 10:46 PM UTC

Breaking updates

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.

Not checkedNot checked

compose.yaml

Upstream Compose changes relative to the verified recipe.

Upstream Compose is not trackedNot checked

Outdated Docker images

Pinned image tags checked for newer versions.

All images are currentAug 30, 2026, 10:46 PM UTC

From observation to action

Update plan

The recipe already pins the latest known release. The steps below show the evidence retained for that version.

v10.11.11v10.11.11
  1. Open the release evidence

    Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.

  2. Review the version boundary

    The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.

  3. Prepare image-tag changes

    No image-tag change is currently planned.

  4. Follow the documented migration shape

    Migration, majors cannot be skipped. Release-specific migration steps still come from the release notes and installation guide.

  5. Protect the rollback point

    No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.

  6. Verify the updated recipe

    An exact Fully verified check for this target was recorded on 2026-08-30.

Read the update procedure in the installation guide ↓

Specifications

Security and exposure

Installation levels

The status shows how fully the published recipe meets each level's requirements.

Ready

For home

A minimal setup for a local network or access through a VPN.

  • Single server
  • Local access
  • Minimum components

The recipe covers every requirement at this level.

Ready

For a VPS

A public deployment with protected access and backups.

  • Domain
  • HTTPS via reverse proxy
  • Off-server backup

The recipe covers every requirement at this level.

Not supported

Reliable

A setup designed for predictable operations and observability.

  • Separate database
  • Healthcheck
  • External monitoring
  • CPU and memory limits

You still need to add: separate database, external monitoring, cpu and memory limits.

Tailored to your server

Configuration generator

Answer five questions and download a ready-to-run Jellyfin bundle.

Runs locally
01 Do you have a domain?
02 Which reverse proxy?
03 Server architecture
04 Where should data be stored?

Use an absolute path for a separate disk, such as /srv/jellyfin.

Mounted read-only: the server cannot modify or delete your files.

05 Is VPN access required?

Your bundle is ready

The archive is created in your browser. Your answers are never sent anywhere.

Screenshots

Screenshots have not been added yet.

Quick start

Read the full guide first: it explains external access, backups, and data locations.

Prepare settingscp .env.example .env
Launchdocker compose up -d
Check statusdocker compose ps

Step-by-step guide

1. Check your Ubuntu or Debian server

For 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

2. Lay out the library the way Jellyfin expects

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

3. Prepare the files and variables

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.

4. Complete the setup wizard

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.

Running on a VPS

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.

Access from a local network

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.

Domain and HTTPS

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

Backup

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.

Restore

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.

Update

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.

Rollback

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.

Stopping and complete removal

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.

Troubleshooting

The container never becomes healthy

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.

The library is empty after a scan

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.

Artwork and descriptions were not fetched

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.

Video does not play or stutters

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.

Seeking breaks behind a reverse proxy

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.

The activity log shows one and the same IP address

That is the reverse proxy address. Add it under Dashboard → Networking → Known proxies and the server will start trusting the X-Forwarded-For header.

Clients do not find the server automatically

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.

The disk is filling up

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.

The server does not start after an update

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.

Official sources

Latest release: 2026-06-06 · GitHub Stars: 56,375 · metadata checked Aug 30, 2026, 10:46 PM UTC

Disclaimer

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.