๐๏ธ Building my home server: Part 7
Streaming movies with Jellyfin
In my previous blog post, I covered setting up centralized logging with Loki and Promtail. In this post, I'm deploying Jellyfin โ a free, open-source media server. The idea is simple: I have a collection of movies on my server, and I want to stream them on my local network from any device with a web browser.
๐ค Why Jellyfin?
Jellyfin is a self-hosted media server that organizes your media library, provides metadata (posters, descriptions, subtitles), and streams content to any device with a web browser or a Jellyfin client app. Think of it as a self-hosted Netflix.
It's the most popular open-source alternative to Plex, with no account requirements and no premium tier โ everything is free. No telemetry, no tracking, no "sign in to continue" prompts. You own your data and your server.
Other options I considered:
- Plex โ the most polished option, but requires an account, has a freemium model, and phones home. Some features (hardware transcoding, mobile sync) are locked behind Plex Pass.
- Emby โ started as open source but went proprietary. Similar to Plex in terms of requiring a license for key features.
- Navidrome โ excellent for music, but doesn't handle video.
Jellyfin checked all the boxes: fully open source, no account wall, good client support, and active development.
๐ณ Running Jellyfin in Docker
Jellyfin runs as a single Docker container. The Ansible playbook uses the docker_container module (consistent with how I deploy all other services in this home lab):
- name: Ensure Jellyfin container is running
docker_container:
name: jellyfin
image: jellyfin/jellyfin
state: started
restart_policy: unless-stopped
published_ports:
- "127.0.0.1:8096:8096"
env:
TZ: "Europe/Budapest"
volumes:
- "/home/jellyfin/config:/config"
- "/home/jellyfin/cache:/cache"
- "/mnt/movies:/media:ro"
A few things worth noting:
Read-only media: The movies directory is mounted as
:ro(read-only). Jellyfin only needs to read the files for playback and metadata scanning โ it should never modify the source media.Localhost binding: Following the same pattern as all my other services, the port is bound to
127.0.0.1and accessed through my Nginx reverse proxy athttps://jellyfin.arcade-lab.io.Config and cache separation: Jellyfin stores its database, metadata, and settings in
/config, and uses/cachefor image resizing and other temporary data. Keeping them separate makes backups cleaner โ you only need to back up/config.
๐ค Automating with Ansible
As with everything else in my home lab, the entire setup is automated with Ansible. The playbook handles the full lifecycle:
- Checks that Docker is installed
- Creates the config and cache directories
- Starts the Jellyfin container with the correct volumes, ports, and environment variables
All paths and configuration values live in a variables file that's excluded from version control, keeping sensitive information out of the repository. Running the playbook on a fresh server gets Jellyfin up and running in a single command.
๐ Outcome
With Jellyfin deployed, I now have:
- On-demand streaming โ a Netflix-like interface for browsing and watching my movie collection from any device on the network.
- Metadata and organization โ Jellyfin automatically fetches posters, descriptions, ratings, and subtitles for my movies, making the library easy to browse.
- Fully automated deployment โ one Ansible playbook sets up everything from directories to the running container.
Noice! ๐
You can also read this post on my portfolio page.
Top comments (0)