How to Install Jellyfin Media Server with Docker (Own Your Media)
In October 2023, Plex announced they were adding a free ad-supported streaming service into their app. The media server I installed to watch my own content now shows me someone else’s ad-supported films in the same interface. In 2024, they removed some features from the free tier. This is the pattern with every platform that starts free and open: eventually your data becomes their product. Jellyfin does not have this problem, because Jellyfin does not have a business model. It is free, open source, and it does not phone home. It is just a media server.
From the homelab: I run Jellyfin as my primary media server, streaming to TVs, phones, and tablets across the house. After years of paying for streaming services that kept removing content I wanted to watch, owning my media library and serving it myself was a no-brainer.

Why Jellyfin Over Plex (or Emby)
I have run all three. Plex for years before it started adding features I did not ask for, Emby briefly before it went proprietary, and Jellyfin since 2021. Here is why Jellyfin wins for self-hosters:
- No account required. Plex requires you to create a Plex account and authenticate through their servers, even to watch content on your own LAN. If Plex’s servers go down, you cannot log in to watch a film on a hard drive sitting three feet from you. Jellyfin authenticates locally. No external dependency.
- No phoning home. Plex sends telemetry data about what you watch, when you watch it, and on which devices. Even with telemetry “disabled” in settings, network analysis shows it still communicates with Plex’s servers. Jellyfin does not contact any external server. Ever.
- Truly free. All features included. No “Plex Pass” tier-gating hardware transcoding, offline sync, or lyrics behind a paywall. No ads, no upsells, no premium features held back.
- Open source. The code is auditable. You can verify every claim I just made. Try doing that with Plex.
The trade-off? Plex has a more polished UI and a larger ecosystem of pre-built client apps. Jellyfin’s ecosystem has grown significantly, but some smart TV apps are still community-maintained. On Roku, Fire TV, iOS, Android, and web browsers, Jellyfin works well. On some niche devices, you might need to use a browser instead of a dedicated app.
For me, that trade-off is not even close. I will take full control over a slightly less polished app every single time.
Career Context: Media servers exercise real infrastructure skills — storage management, transcoding (CPU/GPU resource allocation), networking, user access control, and service reliability. These are the same concepts you manage in enterprise environments with video conferencing infrastructure, digital asset management, and content delivery. Understanding transcoding profiles and hardware acceleration translates directly to multimedia processing pipelines in production.
Prerequisites
You will need:
- Docker and Docker Compose installed. Follow the Docker installation guide if needed.
- Media files. Films, TV shows, or music that you actually own. Ripped from your DVDs, Blu-rays, or purchased DRM-free. Jellyfin does not care where the files came from, but this guide is about owning your media, not pirating it.
- Storage. A media library grows fast. A single Blu-ray rip can be 20-40 GB. Even compressed to 1080p x265, a decent film library eats through a terabyte quickly. An external USB drive works to start. A NAS is the long-term answer.
- CPU or GPU for transcoding (optional but recommended). If every client can direct-play your media format, you need almost no CPU. If clients need transcoding (converting formats on the fly), you need either a decent CPU or a GPU with hardware encoding support. More on this below.
Step 1: Organise Your Media Library
Jellyfin relies heavily on file and folder naming to identify your media and fetch metadata. Get this right from the start and save yourself hours of manual corrections later.
The standard structure:
/media/
movies/
The Matrix (1999)/
The Matrix (1999).mkv
Blade Runner 2049 (2017)/
Blade Runner 2049 (2017).mkv
tv/
Breaking Bad/
Season 01/
Breaking Bad - S01E01 - Pilot.mkv
Breaking Bad - S01E02 - Cat's in the Bag.mkv
Season 02/
Breaking Bad - S02E01 - Seven Thirty-Seven.mkv
music/
Pink Floyd/
The Dark Side of the Moon (1973)/
01 - Speak to Me.flac
02 - Breathe.flac
Key naming rules:
- Movies go in individual folders with the year in parentheses
- TV shows use the
S01E01format, which every metadata provider recognises - Music follows
Artist/Album (Year)/Trackstructure
Pro tip: If you have hundreds of files with inconsistent naming, do not fix them by hand. Use FileBot (there is a Linux version) or tinyMediaManager to bulk-rename against TheMovieDB or TheTVDB. An hour of automated renaming saves days of Jellyfin not recognising your content or pulling the wrong metadata.
Step 2: Create the Docker Compose File
Create a directory for Jellyfin and the compose file:
mkdir -p ~/jellyfin && cd ~/jellyfin
Create docker-compose.yml:
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
ports:
- "8096:8096" # HTTP web UI
- "8920:8920" # HTTPS web UI (optional)
- "1900:1900/udp" # DLNA discovery (optional)
- "7359:7359/udp" # Client auto-discovery
volumes:
- ./config:/config
- ./cache:/cache
- /path/to/your/movies:/media/movies:ro
- /path/to/your/tv:/media/tv:ro
- /path/to/your/music:/media/music:ro
environment:
- JELLYFIN_PublishedServerUrl=http://your-server-ip:8096
Replace /path/to/your/movies, /path/to/your/tv, and /path/to/your/music with the actual paths to your media. The :ro flag mounts them read-only, which is good practice — Jellyfin never needs to write to your media files.
Start it:
docker compose up -d
Visit http://your-server-ip:8096 to begin the setup wizard.
Step 3: Initial Configuration
The setup wizard walks you through the basics:
- Language and first user. Create your admin account. Use a strong password, especially if you plan to expose this remotely later.
- Media libraries. Add your libraries now. Click “Add Media Library,” select the type (Movies, Shows, Music), give it a name, and point it at the mounted path (e.g.,
/media/movies). For metadata, I use TheMovieDB for films and TheTVDB for shows. Enable both and let Jellyfin pick the best match. - Metadata language. Set to English (or your preference). This affects which metadata and artwork Jellyfin downloads.
- Remote access. Leave this enabled if you want to access Jellyfin from outside your network. We will configure the actual remote access properly later.
After completing the wizard, Jellyfin begins scanning your libraries. Depending on library size, this can take anywhere from a few minutes to several hours. Let it run. You can browse partially scanned content while it works.
Metadata downloads are heavy on first scan. Jellyfin fetches artwork, descriptions, cast information, and ratings for every item. For a library of 500 films, expect a few gigabytes of metadata. Make sure your /config volume has enough space. I have seen people put config on a small boot drive and run out of space during the first scan, which corrupts the metadata database.
Step 4: Hardware Transcoding
Transcoding is the process of converting media from one format to another in real time. If you store your films in 4K HEVC and a client only supports 1080p H.264, Jellyfin needs to transcode on the fly. This is CPU-intensive. A single 4K transcode can pin an entire consumer CPU.
Hardware transcoding offloads this to dedicated silicon on your GPU or CPU’s integrated graphics. It is dramatically faster and uses a fraction of the power.
Intel Quick Sync (Most Common for Homelabs)
If your server has an Intel CPU with integrated graphics (most desktop Intel chips from 2015 onwards), Quick Sync is the easiest path. Add the GPU device to your container:
services:
jellyfin:
# ... existing config ...
devices:
- /dev/dri:/dev/dri
group_add:
- "video"
- "render"
In Jellyfin’s dashboard, go to Administration > Playback > Transcoding and select “Intel QuickSync (QSV)” as the hardware acceleration type. Enable HEVC, H.264, and any other codecs your hardware supports.
NVIDIA GPU
If you have an NVIDIA GPU, install the NVIDIA Container Toolkit first:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
Then add to your compose file:
services:
jellyfin:
# ... existing config ...
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
In Jellyfin, select “NVIDIA NVENC” as the hardware acceleration type.
Pro tip: Avoid transcoding entirely where possible. Store your media in widely compatible formats (H.264 in MKV or MP4 containers) and most clients will direct-play without transcoding. Transcoding is a fallback, not the goal. If you are ripping Blu-rays, HandBrake with H.264/AAC output gives you maximum compatibility. HEVC (H.265) saves about 40% disk space but requires transcoding on older clients.
Step 5: User Accounts and Family Sharing
Jellyfin supports multiple user accounts, each with their own watch history, preferences, and access controls. Go to Administration > Users > Add User.
For a family setup:
- Create individual accounts for each family member
- Set parental controls per user (restrict content ratings, hide specific libraries)
- Disable admin access for non-admin users
- Set a maximum number of simultaneous streams if your hardware is limited
You can also restrict which libraries each user can see. If you have a library of films that are not suitable for children, give the kids’ accounts access only to the family-friendly library. This works at the library level, not the individual film level, so organise your content accordingly.
Step 6: Remote Access
Accessing Jellyfin from outside your network gives you three options, and they have very different risk profiles.
Option 1: Tailscale (Recommended)
The simplest and most secure approach. If you have Tailscale set up, your Jellyfin server is already accessible from any device on your tailnet. No ports to open, no reverse proxy to configure, no exposure to the internet. The Jellyfin apps on your phone connect directly via the Tailscale IP. This is how I access mine.
Option 2: Reverse Proxy
If you want a clean URL like media.yourdomain.com, set up a reverse proxy (Nginx Proxy Manager, Caddy, or Traefik) pointing to port 8096, with a Let’s Encrypt certificate for TLS. If you are on a dynamic IP, use DuckDNS to keep your domain pointing at the right address.
This exposes Jellyfin to the public internet, so harden it:
- Use strong passwords for all accounts
- Disable guest access
- Consider putting it behind Authelia or Authentik for an extra authentication layer
- Monitor login attempts
Option 3: VPN
A traditional WireGuard VPN to your home network. More setup than Tailscale, but zero third-party dependency. Once connected to the VPN, Jellyfin is accessible at its LAN IP as if you were at home.
Step 7: Client Apps
Jellyfin has native apps for most platforms:
- Web browser — the built-in web UI works on any modern browser. No install needed.
- Android — official app on Google Play and F-Droid
- iOS — Swiftfin (third-party, excellent) or the official Jellyfin app
- Fire TV / Android TV — official app on the Amazon Appstore and Google Play
- Roku — official Jellyfin channel
- Samsung/LG Smart TVs — community-maintained apps available. Quality varies.
- Desktop — Jellyfin Media Player (based on MPV) for Windows, Mac, and Linux
On each client, you will be asked for the server URL. Enter your Jellyfin server’s IP and port (http://192.168.1.x:8096) or your tailnet/domain URL if accessing remotely.
Pro tip: For the best TV experience, use an Android TV device (Chromecast with Google TV, NVIDIA Shield, or Fire TV Stick 4K) rather than a smart TV’s built-in app. The dedicated streaming devices have better app support, get updates faster, and generally handle direct playback of high-bitrate content more reliably. The NVIDIA Shield is the gold standard for media server clients, but a Fire TV Stick 4K at a quarter of the price handles 95% of use cases.
The Media Sovereignty Angle
I want to take a moment on why this matters beyond just having a nice media setup at home. Every month, streaming services rotate content. Films you paid to “buy” on digital platforms disappear when licensing deals change. Amazon, Apple, and Google have all removed purchased content from users’ libraries. The terms of service you agreed to do not give you ownership — they give you a revocable licence.
A physical disc, ripped to your own server, cannot be revoked. The film does not disappear when a licensing deal expires. It does not get edited after release. It does not require an internet connection or an active subscription to watch. It is yours in the way that digital “purchases” are not.
Jellyfin is the infrastructure that makes physical media ownership practical in a streaming world. It gives you the convenience of Netflix with the ownership of a DVD shelf. That combination is what media sovereignty actually looks like in practice.
Troubleshooting
Media Not Detected or Wrong Metadata
Almost always a naming issue. Jellyfin uses the folder and file names to search metadata providers. If “The Matrix” is named matrix_1999_rip.mkv in a folder called films, Jellyfin will not recognise it. Follow the naming convention in Step 1. After renaming, trigger a library rescan from the admin dashboard.
Transcoding Fails or Stutters
Check that hardware acceleration is working. In the Jellyfin logs (docker logs jellyfin), look for messages about fallback to software transcoding. If it is falling back, your GPU device is either not passed through correctly or the drivers are not installed. Verify with ls -la /dev/dri/ on the host — you should see renderD128 (or similar) for Intel QSV.
Buffering on Remote Playback
Your upload speed is the bottleneck. A 1080p stream needs about 8-15 Mbps. A 4K stream needs 25-40 Mbps. If your home upload is slower than that, either transcode to a lower bitrate (set a bandwidth limit per user in Jellyfin settings) or pre-encode your media at a lower bitrate for remote streaming.
Subtitles Not Displaying
Embedded subtitles (SRT in MKV containers) should work on most clients. Image-based subtitles (PGS, VOBSUB from Blu-ray rips) require transcoding to burn them into the video stream, which triggers a transcode even if the video codec is compatible. If subtitle-triggered transcoding is a problem, extract the subtitles to external SRT files using MKVToolNix or ffmpeg and convert them to text format with SubtitleEdit.
Docker Container Cannot Access Media Files
Permissions. The Jellyfin container runs as a specific user (UID 1000 by default). If your media files are owned by a different user or have restrictive permissions, Jellyfin cannot read them. Check with ls -la /path/to/your/media and ensure the files are readable by the container user. The quick fix: chmod -R o+r /path/to/your/media. The proper fix: set PUID and PGID environment variables in the compose file to match the owner of your media files (Jellyfin’s linuxserver.io image variant supports this).
What to Build Next
You have a functioning media server. Here is what complements it:
- Tailscale VPN — access Jellyfin securely from anywhere without exposing it to the internet. The easiest remote access solution.
- Uptime Kuma Monitoring — monitor Jellyfin’s availability. If it goes down at 11pm on a Friday, you want to know before the family notices.
- Build Your First Homelab — if Jellyfin is your first self-hosted service, this guide helps you plan the infrastructure to support it properly
- Install Docker on Ubuntu 24.04 — the container platform everything runs on
- Vaultwarden Password Manager — another critical piece of self-hosted infrastructure. If you are running Jellyfin, you are already set up for more.
- Grafana and Prometheus Monitoring — deep metrics on your server’s CPU, memory, and disk. Essential when transcoding starts eating resources.
Every film on your Jellyfin server is a film that cannot be removed from your library by a licensing change, a service shutdown, or a terms of service update. That is not a technical achievement. That is ownership.
Watch out for this: If your media is not being picked up, check your folder structure before anything else. Jellyfin is particular about naming conventions — Movie Name (Year)/Movie Name (Year).mkv saves a lot of metadata headaches.
Key Takeaways
- Jellyfin is a fully free, open-source media server with no accounts, no telemetry, and no feature paywalls. It is the self-hosted alternative to Plex and Emby.
- File and folder naming is critical. Follow the standard naming convention from the start and save yourself hours of manual metadata corrections.
- Hardware transcoding (Intel QSV or NVIDIA NVENC) dramatically reduces CPU usage when clients need format conversion. Pass the GPU device through to the container.
- Avoid transcoding where possible by storing media in widely compatible formats (H.264/AAC). Direct play uses almost no server resources.
- Use Tailscale for remote access rather than exposing Jellyfin to the public internet. It is simpler, more secure, and requires zero port forwarding.
- Physical media, ripped and served through Jellyfin, gives you the convenience of streaming with the ownership guarantees that digital purchases do not provide.
- Media sovereignty is not about hoarding content. It is about ensuring that what you paid for cannot be silently removed, edited, or paywalled after the fact.
Related Guides
If you found this useful, these guides continue the journey:
- Docker Compose for Beginners — manage Jellyfin alongside your other containers
- Nginx Proxy Manager Setup — access Jellyfin remotely with HTTPS
- Build Your First Homelab in 2026 — the complete starting guide
- Essential Docker Commands — troubleshoot and manage your containers
- Uptime Kuma Monitoring — monitor Jellyfin uptime alongside your other services

ReadTheManual is run, written and curated by Eric Lonsdale.
Eric has over 20 years of professional experience in IT infrastructure, cloud architecture, and cybersecurity, but started with PCs long before that.
He built his first machine from parts bought off tables at the local college campus, hoping they worked. He learned on BBC Micros and Atari units in the early 90s, and has built almost every PC he’s used between 1995 and now.
From helpdesk to infrastructure architect, Eric has worked across enterprise datacentres, Azure environments, and security operations. He’s managed teams, trained engineers, and spent two decades solving the problems this site teaches you to solve.
ReadTheManual exists because Eric believes the best way to learn IT is to build things, break things, and actually read the manual. Every guide on this site runs on infrastructure he owns and maintains.
Enjoyed this guide?
New articles on Linux, homelab, cloud, and automation every 2 days. No spam, unsubscribe anytime.

