Self-Hosting Jellyfin Media Server on a Raspberry Pi: Hardware Transcoding and Remote Access
Kodi turns a Pi into a great media player, but it's a client, not a server — if you've got a family with multiple TVs, phones, and tablets that all need access to the same movie and TV collection, you want a media server instead: one Pi holding the library and streaming it to whatever device asks, transcoding on the fly when a client can't play the source format directly. Jellyfin is the open-source, fully self-hosted answer to Plex — no account required, no cloud dependency, and (unlike Plex) no paid tier gating hardware transcoding. This project walks through installing Jellyfin on a Raspberry Pi, setting up hardware-accelerated transcoding so a Pi 4 or 5 can actually keep up with multiple simultaneous streams, organizing your library, and reaching it securely from outside your home network.
Why Jellyfin Over Plex or Emby on a Pi
Plex's hardware transcoding is now locked behind a Plex Pass subscription, which matters a lot on a Pi — software-only transcoding on Pi-class hardware buckles under anything more demanding than a single 1080p stream. Jellyfin's hardware transcoding via the Pi's VideoCore GPU is free and built in. Emby sits in between (free client, paid server features). For a fully self-hosted setup with no ongoing subscription and no phone-home requirement, Jellyfin is the clear choice, and it's what this build uses.
Installing Jellyfin via Docker
Docker is the cleanest install path on a Pi — it isolates Jellyfin's dependencies and makes hardware device passthrough for transcoding a single config line rather than a system-wide driver install.
curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER # log out and back in for the group change to take effect mkdir -p ~/jellyfin/{config,cache,media}Create a docker-compose.yml:
services: jellyfin: image: jellyfin/jellyfin container_name: jellyfin network_mode: host volumes: - /home/pi/jellyfin/config:/config - /home/pi/jellyfin/cache:/cache - /home/pi/jellyfin/media:/media devices: - /dev/dri:/dev/dri # hardware video acceleration restart: unless-stopped docker compose up -dBrowse to http://<pi-ip>:8096 to run the first-time setup wizard: create an admin account, point Jellyfin at your /media mount, and let it scan and fetch metadata for your library.
Hardware Transcoding on Raspberry Pi
Software transcoding one 1080p H.264 stream on a Pi 4 is borderline; two simultaneous transcodes will stutter. The Pi's VideoCore VII (Pi 5) / VideoCore VI (Pi 4) GPU can do this in hardware through V4L2, and Jellyfin supports it directly:
- In Jellyfin, go to Dashboard → Playback and set Hardware Acceleration to Video4Linux2 (V4L2M2M).
- Enable decoding for H.264 and HEVC if your library includes it.
- Confirm /dev/dri exists on the host (ls /dev/dri) and that it's passed into the container as shown in the compose file above — without this line, Jellyfin silently falls back to software transcoding and you won't get an error, just poor performance.
Realistically, plan for a Pi 5 (4GB or 8GB) if more than one person will stream simultaneously, or if your library includes 4K content that needs downscaling for phones and older TVs — a Pi 4 can handle direct play (no transcoding) of almost anything, but transcoding load scales fast with resolution and stream count.
Storage: Don't Put Your Library on the SD Card
A media library belongs on external storage, not the boot SD card — both for capacity and because heavy sequential read/write during scanning and transcoding accelerates SD card wear. A USB 3.0 SSD is the sweet spot for a Pi: fast enough to saturate gigabit Ethernet, no spin-up delay, and quiet. For a larger archival library, a USB 3.0 hard drive enclosure works fine since video streaming is sequential-read-friendly even on spinning disks. If you're also running Frigate or other Pi projects that hammer storage, keep Jellyfin's media on its own drive rather than sharing with the OS or other services' I/O load.
Organizing Your Library for Correct Metadata
Jellyfin's metadata scraper expects a specific folder naming convention — get this wrong and you'll get missing posters, wrong episode matches, or duplicate entries:
/media/movies/Movie Name (2023)/Movie Name (2023).mkv /media/tv/Show Name/Season 01/Show Name S01E01.mkvTools like Filebot or a simple shell script using mediainfo can batch-rename an existing messy collection into this structure before your first library scan — much faster than fixing matches one at a time in the Jellyfin UI afterward.
Remote Access Without Port-Forwarding Risk
Opening port 8096 directly to the internet is not recommended — it puts Jellyfin's attack surface directly in front of anyone scanning for it. Two safer options, both of which pair well with the Pi VPN and reverse proxy content already on this site:
- Tailscale or WireGuard (see our Pi VPN guides): connect from outside as if you were on the home network, no public exposure at all. This is the simplest and most secure option for personal/family use.
- Nginx reverse proxy with a real TLS certificate if you specifically want a public URL to share with people outside your household — requires more care around Jellyfin's own account security and keeping it patched.
Keeping It Fed: *arr Stack Integration
Jellyfin plays what's in your library but doesn't manage acquiring or organizing new content — that's the job of the Sonarr/Radarr/Prowlarr ("*arr") stack, which can run as additional Docker containers alongside Jellyfin on the same Pi and automatically rename, move, and notify Jellyfin when new episodes or movies land in your media folders. That's a project of its own, but it's worth knowing the pieces fit together on the same box if you want a fully automated setup down the line.
Once it's running, a Jellyfin box on a Pi 5 with an SSD and hardware transcoding enabled comfortably handles a household's worth of simultaneous streams for the cost of the hardware alone — no subscription, no cloud account, and your library stays on your own network.
Related Guides
- Self-Hosted CI/CD Runner on a Raspberry Pi: GitHub Actions and Gitea Actions
- Build a Raspberry Pi Kubernetes Cluster with K3s: A Hands-On Way to Learn Distributed Systems
- Build a Local AI Security Camera System with Frigate NVR on a Raspberry Pi
- Self-Host Your Photos with Immich: A Local Google Photos Alternative
- How to Use the ESP32-CAM: Video Streaming, Motion Detection, and Time-Lapse
- How to Install Klipper on Any 3D Printer: Complete Setup Guide
- How to Set Up OpenCV Machine Vision on a Raspberry Pi
- Raspberry Pi: Complete Headless Setup Guide (No Monitor Needed)