Turn a Raspberry Pi into a Media Server
Set up Jellyfin on a Raspberry Pi 4 or 5 with persistent external storage mounts and honest guidance on hardware-accelerated transcoding limits.
Before you start
- ▸Raspberry Pi 4 or 5 with at least 4 GB RAM and a microSD card (16 GB minimum)
- ▸USB hard drive or SSD for media storage
- ▸SSH access or a keyboard/monitor connected to the Pi
- ▸Basic familiarity with the Linux command line
A Raspberry Pi 4 or 5 makes a capable, low-power media server for a home network. This guide walks through setting up Jellyfin (free, open-source, no account required) on Raspberry Pi OS or Ubuntu Server, mounting external storage reliably with systemd, and squeezing out hardware-accelerated transcoding where the hardware actually supports it. Plex notes are included where the setup diverges.
Hardware Requirements and Honest Expectations
The Pi 4 (4 GB or 8 GB RAM) and Pi 5 handle direct-play of 1080p content without breaking a sweat. Software transcoding on ARM is a different story — a Pi 4 can typically handle one 1080p transcode at roughly 4–6 fps, which is too slow for smooth playback. The Pi 5 is meaningfully faster but still not a transcoding powerhouse. The practical advice: organize your library so clients direct-play, and treat transcoding as an occasional fallback.
The Pi 4's VideoCore VI GPU does support H.264 hardware decode via V4L2/MMAL, but Jellyfin's VAAPI path on ARM is unreliable on the Pi 4. The Pi 5's GPU situation is still maturing in upstream drivers as of 2024. Set expectations accordingly — this is covered in the transcoding section.
Prepare the Operating System
Use the Raspberry Pi Imager to flash Raspberry Pi OS Lite (64-bit) or Ubuntu Server 24.04 LTS (arm64). The 64-bit OS is required for Jellyfin's official ARM64 packages. Enable SSH in the imager's advanced options before flashing.
After first boot, update the system:
sudo apt update && sudo apt full-upgrade -y
sudo reboot
Mount External Storage
USB hard drives or SSDs are the practical choice for media libraries. Plugging in and relying on /dev/sdX paths is fragile — use UUIDs and a systemd mount unit instead.
Find the Drive's UUID
lsblk -f
Note the UUID of your media partition (e.g., the ext4 or exfat partition on the USB drive). It will look like a1b2c3d4-....
Format the Drive (if needed)
If the drive is new or needs reformatting, use ext4 for a Linux-only setup. Skip this if your drive is already formatted.
sudo mkfs.ext4 -L mediadrive /dev/sda1
Create the Mount Point and systemd Unit
sudo mkdir -p /mnt/media
Create a systemd mount unit. The filename must match the mount path with slashes replaced by dashes:
sudo nano /etc/systemd/system/mnt-media.mount
[Unit]
Description=External Media Drive
After=local-fs.target
[Mount]
What=UUID=YOUR-UUID-HERE
Where=/mnt/media
Type=ext4
Options=defaults,noatime
[Install]
WantedBy=multi-user.target
Enable and start it:
sudo systemctl daemon-reload
sudo systemctl enable --now mnt-media.mount
systemctl status mnt-media.mount
Create a media subdirectory and set permissions so Jellyfin can read it:
sudo mkdir -p /mnt/media/library
sudo chown -R $USER:$USER /mnt/media/library
Install Jellyfin
Jellyfin provides an official repository for Debian/Ubuntu-based systems, which covers both Raspberry Pi OS and Ubuntu Server.
curl -fsSL https://repo.jellyfin.org/install-debuntu.sh | sudo bash
This script adds the repo, imports the GPG key, and installs jellyfin. Jellyfin's service is managed by systemd and starts automatically:
sudo systemctl enable --now jellyfin
systemctl status jellyfin
Verify it is listening:
ss -tlnp | grep 8096
Initial Web Setup
From a browser on the same network, open http://<pi-ip>:8096. Walk through the setup wizard — create an admin account and point the library at /mnt/media/library. Choose the correct content type (Movies, TV Shows, Music) for each folder you add.
Plex Alternative
If you prefer Plex, download the .deb for ARM64 from plex.tv/media-server-downloads and install it manually:
sudo dpkg -i plexmediaserver_*.deb
sudo systemctl enable --now plexmediaserver
Plex's web UI is at http://<pi-ip>:32400/web. Plex requires a free account for initial setup and has a paywall for some remote-access and sync features. Jellyfin has none of these restrictions.
Hardware-Accelerated Transcoding
This is the most frequently misunderstood topic for Pi media servers. Here is what actually works:
- Pi 4 — H.264 decode via V4L2: Jellyfin has experimental V4L2 support. Go to Dashboard → Playback → Transcoding and set the hardware acceleration to V4L2. It can help with H.264 but is unreliable for H.265/HEVC and does not support encoding. Results vary by Jellyfin version.
- Pi 5 — VAAPI: Early VAAPI support via the
v3ddriver is emerging but not stable as of mid-2024. Monitor the Jellyfin GitHub for Pi 5 VAAPI progress before relying on it. - Practical recommendation: Leave hardware acceleration off and instead configure your clients (smart TVs, Kodi, Infuse) to direct play. In Jellyfin's user settings, set the maximum streaming bitrate high (e.g., 120 Mbps) and ensure the client supports the codec natively.
To add the Jellyfin service user to the video and render groups (needed if you do enable V4L2/VAAPI):
sudo usermod -aG video,render jellyfin
sudo systemctl restart jellyfin
Open the Firewall
Raspberry Pi OS and Ubuntu Server ship without a firewall active by default, but if you have enabled ufw:
sudo ufw allow 8096/tcp comment 'Jellyfin HTTP'
sudo ufw allow 8920/tcp comment 'Jellyfin HTTPS'
sudo ufw reload
For Plex, allow port 32400/tcp instead.
Keep Jellyfin Updated
Because Jellyfin was installed via an apt repository, standard updates apply:
sudo apt update && sudo apt upgrade -y
Verification
- Confirm the mount is active:
systemctl is-active mnt-media.mountshould returnactive. - Confirm Jellyfin is running:
systemctl is-active jellyfin. - Drop a test file into
/mnt/media/library/, trigger a library scan in the Jellyfin dashboard, and confirm the title appears. - Play the file from a second device. Check the Jellyfin dashboard's active sessions — if it shows Direct Play, no transcoding is occurring and you are in the best possible state.
Troubleshooting
Drive Not Mounting at Boot
If the mount unit fails after a reboot, add x-systemd.automount to the Options line in the mount unit, or add a corresponding .automount unit. Also ensure the UUID did not change (it should not for a formatted partition, but NTFS drives can sometimes reassign under Windows).
Jellyfin Can't Read Media Files
Check that the jellyfin user has read access: sudo -u jellyfin ls /mnt/media/library. If it fails, the ownership or permissions on the mount point need adjustment. For drives formatted as exFAT, install exfatprogs and specify Type=exfat in the mount unit.
Playback Stutters or Buffers
The most common cause is transcoding being triggered unexpectedly. In Jellyfin, go to Admin → Dashboard → Active Devices during playback and look at the session. If it shows Transcoding, the client does not support the codec or the bitrate is capped too low. Raise the client's quality setting or install a client app that supports the codec natively (e.g., Jellyfin for Android, Infuse on Apple TV).
Service Crashes After Upgrade
Check the journal: journalctl -u jellyfin -n 50 --no-pager. Configuration incompatibilities after major version upgrades occasionally require clearing the transcoding temp directory: sudo rm -rf /var/lib/jellyfin/transcodes/*.
Frequently asked questions
- Can the Raspberry Pi 4 transcode 4K content?
- Not reliably. The Pi 4 lacks hardware HEVC decode support in Jellyfin's current VAAPI/V4L2 path, and software transcoding of 4K HEVC is far too slow. Store 4K files and use a client that can direct-play them, such as an Apple TV with Infuse or a device with native H.265 support.
- Should I use Jellyfin or Plex on a Raspberry Pi?
- Jellyfin is generally the better fit: no account required, no paywall, and official ARM64 packages via apt. Plex works but requires a free account, and some features (downloads, certain remote access modes) need a Plex Pass subscription.
- Why use a systemd mount unit instead of /etc/fstab?
- A systemd mount unit integrates with dependency ordering — Jellyfin's service unit can declare it needs the mount before starting. It also gives cleaner failure logging via journalctl compared to a silent fstab boot failure.
- My USB drive is formatted as NTFS — will this work?
- Yes, but install the ntfs-3g package first (sudo apt install ntfs-3g) and set Type=ntfs-3g in your mount unit. Write performance is slower than ext4, and you cannot set Linux file permissions natively on NTFS, which may require the uid/gid mount options to give Jellyfin access.
- How do I access Jellyfin outside my home network?
- The safest approach is a VPN back to your home network (WireGuard on the Pi works well). Direct port-forwarding port 8096 is not recommended. If you need public access, put Jellyfin behind a reverse proxy like Caddy with automatic HTTPS and consider fail2ban in front of it.
Related guides
Configure Prometheus Alertmanager
Configure Prometheus Alertmanager with routing trees, receivers, inhibition rules, grouping, Go templates, and PagerDuty/Slack on-call integrations.
Build an Intranet Server on Linux
Set up a complete small-office intranet on one Linux box: Nginx web server, dnsmasq local DNS, Samba file sharing, and a Wiki.js team wiki.
Build an nftables Firewall Script
Build a complete nftables firewall from scratch: tables, chains, sets, default-deny input policy, service allowlisting, and persistent systemd configuration.
Caddy as a Reverse Proxy
Set up Caddy as a reverse proxy with automatic HTTPS, load balancing, WebSocket passthrough, reusable snippets, and header control — no certbot required.