Jellyfin Media Server on Raspberry Pi — Self-Hosted Streaming
Set up Jellyfin on Raspberry Pi to stream movies, TV shows, and music to all your devices. Covers Docker install, hardware transcoding, and remote access.
Introduction
Jellyfin is a free, open-source media server that lets you stream your personal movie, TV show, and music collection to any device—without relying on cloud services or paying subscription fees. Unlike Plex, Jellyfin doesn't require an account to stream on your home network, contains no telemetry, and is completely self-hosted.
Running Jellyfin on a Raspberry Pi is an ideal way to create a personal streaming server that's always on, energy-efficient, and under your complete control. Whether you want to watch your 4K movie collection, access your music library from anywhere, or share media with family, Jellyfin on Pi is a powerful, cost-effective solution.
Why Choose Jellyfin Over Plex?
- No account required for local streaming
- Completely free with no premium tier or upsell
- Open source — inspect the code, no hidden tracking
- Zero telemetry — your viewing habits stay private
- Self-hosted — all your data remains on your hardware
- No remote access limitations — set up your own reverse proxy
Prerequisites
Before starting, ensure you have:
- Raspberry Pi 4 or 5 (Pi 4 with 4GB minimum, Pi 5 recommended for 4K transcoding)
- USB SSD or external HDD with at least 500GB (preferably 2TB+) for media storage
- Ethernet connection (recommended for stability, though Wi-Fi works)
- Docker and Docker Compose installed on your Pi
- Organized media folder structure ready (we'll create this in Step 1)
- Basic Linux command-line knowledge
A Pi 4 with 4GB RAM can handle ~5 concurrent streams with transcoding. A Pi 5 with 8GB RAM is better for 4K content or higher concurrency.
Step 1 — Set Up Storage
Mount External USB Drive
Connect your USB SSD or HDD to the Raspberry Pi. List available disks:
sudo lsblk
Identify your external drive (typically /dev/sda or /dev/sdb). Create a partition and format it:
sudo parted /dev/sda mklabel gpt
sudo parted /dev/sda mkpart primary ext4 0% 100%
sudo mkfs.ext4 /dev/sda1
Create a mount point and mount the drive:
sudo mkdir -p /mnt/media
sudo mount /dev/sda1 /mnt/media
sudo chown $USER:$USER /mnt/media
Persistent Mounting with /etc/fstab
To auto-mount on boot, get the UUID:
sudo blkid /dev/sda1
Add this line to /etc/fstab:
UUID=your-uuid-here /mnt/media ext4 defaults,nofail 0 2
Create Media Folder Structure
mkdir -p /mnt/media/{movies,tv,music,photos}
chmod 755 /mnt/media/*
Organize your media:
/mnt/media/movies/— Individual movie files or folders (Movie Name (Year)/Movie Name.mkv)/mnt/media/tv/— TV shows (Show Name/Season 01/Show Name - s01e01.mkv)/mnt/media/music/— Music organized by artist or album/mnt/media/photos/— Photo collections
Step 2 — Install Jellyfin with Docker Compose
Create a directory for your Docker configuration:
mkdir -p ~/jellyfin-docker
cd ~/jellyfin-docker
Create docker-compose.yml:
version: '3.8'
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
hostname: jellyfin
restart: unless-stopped
# Device passthrough for hardware transcoding
devices:
- /dev/video10:/dev/video10 # V4L2 device for transcoding
- /dev/video11:/dev/video11 # Secondary V4L2 device
# Volume mounts
volumes:
- /home/pi/.config/jellyfin:/config
- /home/pi/.cache/jellyfin:/cache
- /mnt/media/movies:/media/movies:ro
- /mnt/media/tv:/media/tv:ro
- /mnt/media/music:/media/music:ro
- /mnt/media/photos:/media/photos:ro
# Network and ports
ports:
- "8096:8096" # HTTP port
- "8920:8920" # HTTPS port
- "7359:7359/udp" # Client discovery
- "1900:1900/udp" # DLNA discovery
environment:
- JELLYFIN_PublishedServerUrl=http://192.168.1.100:8096
- TZ=UTC
# Resource limits
mem_limit: 2g
cpus: '3.0'
# User permissions
user: "1000:1000"
# Optional: reverse proxy for remote access
caddy:
image: caddy:latest
container_name: caddy-jellyfin
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
environment:
- ACME_AGREE=true
depends_on:
- jellyfin
volumes:
caddy_data:
caddy_config:
Start the container:
docker-compose up -d
Check logs:
docker-compose logs -f jellyfin
Step 3 — Initial Setup
Access Jellyfin Web Interface
Open your browser and go to http://your-pi-ip:8096. You'll see the setup wizard.
Create Your First User
- Set your server name (e.g., "HomeMedia")
- Create an admin user with a strong password
- Choose your preferred display language
- Set metadata language (English recommended for accuracy)
Add Media Libraries
- Go to Admin → Libraries
- Click Add Library
- For Movies:
- Display Name: "Movies"
- Content Type: "Movies"
- Add Folder:
/media/movies - Enable auto-organize and metadata fetching
- Repeat for TV Shows (Content Type: "Shows")
- Repeat for Music (Content Type: "Music")
- Repeat for Photos (Content Type: "Photos")
Click Scan All to index your media. This may take 30+ minutes for large libraries on a Pi.
Step 4 — Hardware Transcoding on Raspberry Pi
Hardware transcoding offloads video encoding to the Pi's GPU, reducing CPU load and power consumption.
Enable V4L2 Transcoding
In Admin → Playback:
- Enable "Transcoding"
- Set Transcode Threads to
2(Pi 4) or4(Pi 5) - Video Decoder: "h264" (or "hevc" if available)
- Video Encoder: Select "hevc_v4l2m2m" or "h264_v4l2m2m"
Set bitrate limits based on your network and client:
- Remote streams: 5 Mbps (quality 720p)
- Local streams: 20 Mbps (quality 1080p)
Direct Play vs Transcoding
Direct Play (no transcoding):
- Client can play the video format natively
- Fastest, lowest CPU/power usage
- Best for local network streaming
Transcode When Needed:
- Client doesn't support the codec
- Bitrate needs to be reduced (remote streaming over slow connections)
- Resolution downscaling required
Jellyfin automatically detects client capabilities and uses direct play whenever possible.
Step 5 — Install Jellyfin Clients
Web Browser
Simply visit http://your-pi-ip:8096 from any browser. The web client works on desktop, laptop, and tablet.
Mobile Apps
- iOS: Download from App Store (search "Jellyfin")
- Android: Download from Google Play Store or F-Droid
- Set server URL to your Pi's local IP:
http://192.168.1.100:8096
Smart TV and Streaming Devices
- Roku: Search for "Jellyfin" in the Roku Channel Store
- Amazon Fire TV: Available in the Appstore
- Android TV: Available on Google Play
- LG/Samsung Smart TV: Install via WebOS/Tizen app stores
- Apple TV: Available on tvOS App Store
- Kodi: Install Jellyfin addon (better compatibility with advanced features)
Each client stores the server address in its settings, so configure once and stream everywhere.
Step 6 — Remote Access
Option A: Reverse Proxy with Caddy
Use the Caddy service in the docker-compose.yml above. Create a Caddyfile:
jellyfin.yourdomain.com {
reverse_proxy jellyfin:8096 {
header_up X-Real-IP {http.request.remote}
header_up X-Forwarded-For {http.request.remote}
header_up X-Forwarded-Proto {http.request.proto}
}
# Encode responses
encode gzip
# Security headers
header / {
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
X-XSS-Protection "1; mode=block"
}
}
Point your domain's DNS A record to your home IP, then:
docker-compose up -d caddy
Caddy will auto-generate an SSL certificate and handle HTTPS.
Option B: Tailscale for Secure Remote Access
Tailscale is easier if you don't own a domain or run a public server.
Install on your Pi:
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
Install Tailscale on your phone/laptop, then access Jellyfin via your Pi's Tailscale IP (e.g., http://100.x.x.x:8096). All traffic is encrypted end-to-end.
No port forwarding, no DDoS exposure, no dynamic DNS needed.
Library Management
Naming Conventions
Jellyfin uses metadata agents (The Movie Database, The TV Database) to match and enrich your files. Proper naming is crucial:
Movies:
/mnt/media/movies/The Matrix (1999)/The Matrix (1999).mkv
/mnt/media/movies/Inception (2010)/Inception (2010).mp4
TV Shows:
/mnt/media/tv/Breaking Bad/Season 01/Breaking Bad - s01e01 - Pilot.mkv
/mnt/media/tv/The Office/Season 02/The Office - s02e15 - Boys and Girls.mkv
Subtitle Files
Place subtitles in the same folder as the video with matching names:
Movie.mkv
Movie.srt (or .ass, .ssa, .vtt, .sub)
For TV shows:
Show - s01e01.mkv
Show - s01e01.en.srt
Show - s01e01.es.srt
Metadata Agents
In Admin → Metadata, choose your preferred providers:
- The Movie Database (TMDB) — best for movies
- The TV Database (TVDB) — best for TV shows
- Music Brainz — music metadata
Allow Jellyfin to fetch metadata after scanning. Manual matching is available if auto-match fails.
Performance Tips
Prefer Direct Play
The Pi's CPU is its bottleneck. Minimize transcoding:
Use device-compatible formats:
- H.264 video (most devices support)
- AAC or MP3 audio
- MP4 container
Set client playback preferences — ask clients to request direct play when possible
Limit remote bitrate to avoid transcoding:
- 5 Mbps for 720p over 4G
- 15 Mbps for 1080p over home broadband
SSD vs HDD Impact
- HDD (~$20-40/TB): Slower library scanning, higher latency, but adequate for streaming. Fine for movies/TV.
- SSD (~$50-100/TB): Instant library access, faster scanning. Better for large libraries or high concurrent streams. Recommended if budget allows.
Media library size vs RAM (rough estimates):
- 500 movies: ~200MB RAM
- 5,000 songs: ~150MB RAM
- 100 TV seasons: ~300MB RAM
A Pi 4 with 4GB RAM comfortably handles 1,500+ items.
RAM and Cache
Set container memory limit to avoid OOM:
mem_limit: 2g # Adjust based on your Pi's total RAM
Jellyfin uses cache for metadata, artwork, and recently accessed libraries. Increase /cache volume size if you have fast storage.
Jellyfin vs Plex vs Emby
| Feature | Jellyfin | Plex | Emby |
|---|---|---|---|
| Cost | Free | Free (with premium) | $5/month or one-time $150 |
| Server Account | Not required | Required (free) | Required (paid) |
| Self-Hosted | Yes | Yes | Yes |
| Open Source | Yes | No | No |
| Telemetry | None | Yes | Minimal |
| Hardware Transcoding | Yes | Yes | Yes |
| Mobile Apps | Free (community) | Free | Free (trial) / Paid |
| Remote Access | Manual setup | Built-in (requires account) | Built-in (requires purchase) |
| Library Size Limit | None | Unlimited | Unlimited |
| Recommendations | Basic | ML-based | ML-based |
Best for: Jellyfin wins on privacy and freedom. Plex wins on convenience and recommendations. Emby is middle ground.
Troubleshooting
Buffering or Playback Stops
Cause: Transcoding too slow or network congestion
Solution:
- Check CPU usage:
docker stats jellyfin - Reduce transcode bitrate in Admin → Playback
- Ask clients to prefer direct play
- Use Ethernet instead of Wi-Fi
- Check network for interference (microwave, neighbor's Wi-Fi)
Hardware Transcoding Not Working
Cause: V4L2 device not detected
Solution:
# Check available video devices
ls /dev/video*
# If none exist, rebuild with newer kernel
sudo apt update && sudo apt upgrade -y
sudo reboot
Verify in Admin → Playback that encoder shows "h264_v4l2m2m" or "hevc_v4l2m2m".
Subtitles Not Showing
Cause: Incorrect file naming or unsupported format
Solution:
- Rename subtitles to match video:
Movie.srt(notMovie.en.srt) - Use SRT or ASS format (most compatible)
- In player settings, manually select subtitle stream
- Check Admin → Playback for subtitle encoder settings
Clients Can't Find Server on Network
Cause: Discovery disabled or firewall blocking
Solution:
- Ensure server is running:
docker-compose ps - Check firewall allows port 8096:
sudo ufw allow 8096 - Manually add server in client settings:
http://192.168.1.100:8096 - Use static IP for Pi to prevent discovery issues
- Check DLNA is enabled in Admin → Playback (for some devices)
Library Scan Takes Hours
Cause: Large library on slow storage or weak CPU
Solution:
- Use SSD instead of HDD if possible
- Exclude unwanted folders (behind the library → right-click → settings)
- Disable "Automatically add missing episodes" during initial scan
- Reduce number of libraries in scan queue
Summary
Jellyfin on Raspberry Pi is a powerful, private alternative to cloud streaming services. With proper storage setup, hardware transcoding, and security measures, you can stream your entire media collection to any device—without monthly fees or external accounts.
The beauty of Jellyfin is its simplicity: install, organize, and stream. Whether you're accessing your movies at home or securely via Tailscale from abroad, Jellyfin gives you full control over your media.
Start small with a few hundred movies or seasons, then scale up as you gain confidence. The community is active and helpful, and new features arrive regularly. Your media, your hardware, your rules—that's the Jellyfin promise.
Ready to get started? Install Docker on your Pi, follow these steps, and enjoy commercial-free, censorship-free streaming within the hour. Questions? Check the Jellyfin documentation or post in their active community forums.