Tools: 🏗️ Building my home server P7: Streaming movies with Jellyfin

Tools: 🏗️ Building my home server P7: Streaming movies with Jellyfin

🏗️ Building my home server: Part 7

🤔 Why Jellyfin?

🐳 Running Jellyfin in Docker

🤖 Automating with Ansible

🎉 Outcome 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. 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: Jellyfin checked all the boxes: fully open source, no account wall, good client support, and active development. 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): 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.1 and accessed through my Nginx reverse proxy at https://jellyfin.arcade-lab.io. Config and cache separation: Jellyfin stores its database, metadata, and settings in /config, and uses /cache for image resizing and other temporary data. Keeping them separate makes backups cleaner — you only need to back up /config. As with everything else in my home lab, the entire setup is automated with Ansible. The playbook handles the full lifecycle: 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. With Jellyfin deployed, I now have: You can also read this post on my portfolio page. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Code Block

Copy

- 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" - 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" - 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" - 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. - 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.1 and accessed through my Nginx reverse proxy at https://jellyfin.arcade-lab.io. - Config and cache separation: Jellyfin stores its database, metadata, and settings in /config, and uses /cache for image resizing and other temporary data. Keeping them separate makes backups cleaner — you only need to back up /config. - Checks that Docker is installed - Creates the config and cache directories - Starts the Jellyfin container with the correct volumes, ports, and environment variables - 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.