Run Jellyfin with Docker on Ubuntu

The Docker layout I use for Jellyfin, including mounts, permissions, updates, and backups.

What this setup does

The important files live on the host. The container can be replaced without losing the library or settings.

The three paths to understand: /config stores the Jellyfin database and settings, /cache stores temporary and generated data, and /media is the library you want Jellyfin to scan.

1. Prepare the folders

Use folders that are easy to recognize and back up. This example keeps Jellyfin state in your home directory and media in a separate Media folder.

mkdir -p ~/jellyfin/config ~/jellyfin/cache
mkdir -p ~/Media/Movies ~/Media/Shows

ls -ld ~/jellyfin/config ~/jellyfin/cache ~/Media

The final ls is not busywork. It confirms the folders exist before Docker creates something unexpected as root.

2. Start the container

First confirm Docker works:

docker --version
sudo docker ps

Then run Jellyfin. Replace YOUR_USERNAME with the Linux account that owns the folders.

sudo docker run -d   --name jellyfin   --network host   --restart unless-stopped   --user "$(id -u YOUR_USERNAME):$(id -g YOUR_USERNAME)"   -v /home/YOUR_USERNAME/jellyfin/config:/config   -v /home/YOUR_USERNAME/jellyfin/cache:/cache   -v /home/YOUR_USERNAME/Media:/media   jellyfin/jellyfin

With host networking, Jellyfin normally listens on port 8096. Open http://SERVER-IP:8096 from another device on the same network.

Do not copy a username from someone else’s command. A wrong host path can create an empty library, while a wrong UID or GID can make the library readable but not writable.

3. Verify mounts and permissions

Check the container status and recent logs:

sudo docker ps --filter name=jellyfin
sudo docker logs --tail 100 jellyfin

Then inspect what the container can actually see:

sudo docker exec jellyfin sh -lc '
  id
  echo "--- media root ---"
  ls -la /media
  echo "--- shows ---"
  find /media/Shows -maxdepth 2 -type d | head -30
'

If a show exists on the host but not inside /media, the bind mount is wrong. If the show is visible but Jellyfin cannot delete or update files, check ownership and permissions on the host:

namei -l /home/YOUR_USERNAME/Media/Shows
find /home/YOUR_USERNAME/Media/Shows -maxdepth 2 -printf '%M %u:%g %p
' | head -40

4. Add libraries carefully

In the Jellyfin dashboard, add a Movies library pointing to /media/Movies and a Shows library pointing to /media/Shows. Use the paths from inside the container, not the host paths.

Keep folders predictable:

/media/Movies/Movie Name (2025)/Movie Name (2025).mkv
/media/Shows/Show Name/Season 01/Show Name - S01E01.mkv

After moving or renaming a large library, scan it once and let that finish before repeatedly removing and re-adding the same path.

5. Put HTTPS in front of it

For public access, use a reverse proxy and a domain rather than exposing Jellyfin’s raw port directly. A minimal Caddy site on the same machine looks like this:

jellyfin.example.com {
    reverse_proxy 127.0.0.1:8096
}

If Jellyfin reaches Caddy through a tunnel, proxy to the tunnel’s local listening port instead. Validate before reloading:

sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

6. Update without losing data

Because the important data is mounted from the host, the container can be replaced safely:

sudo docker pull jellyfin/jellyfin
sudo docker stop jellyfin
sudo docker rm jellyfin

Run the same docker run command again. Do not change the mount paths unless you intend to move the data.

7. Back up the configuration

Stop the container before copying the database so the backup is consistent:

sudo docker stop jellyfin

tar -czf "$HOME/jellyfin-backup-$(date +%F).tar.gz"   -C "$HOME/jellyfin" config

sudo docker start jellyfin

The media itself may be much larger and should have its own backup plan. At minimum, keep a copy of the configuration folder before database repairs or major upgrades.

Official source