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.

Andreas · April 13, 2026 · 10 min read

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

  1. Set your server name (e.g., "HomeMedia")
  2. Create an admin user with a strong password
  3. Choose your preferred display language
  4. Set metadata language (English recommended for accuracy)

Add Media Libraries

  1. Go to Admin → Libraries
  2. Click Add Library
  3. For Movies:
    • Display Name: "Movies"
    • Content Type: "Movies"
    • Add Folder: /media/movies
    • Enable auto-organize and metadata fetching
  4. Repeat for TV Shows (Content Type: "Shows")
  5. Repeat for Music (Content Type: "Music")
  6. 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

  1. In Admin → Playback:

    • Enable "Transcoding"
    • Set Transcode Threads to 2 (Pi 4) or 4 (Pi 5)
    • Video Decoder: "h264" (or "hevc" if available)
    • Video Encoder: Select "hevc_v4l2m2m" or "h264_v4l2m2m"
  2. 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:

  1. Use device-compatible formats:

    • H.264 video (most devices support)
    • AAC or MP3 audio
    • MP4 container
  2. Set client playback preferences — ask clients to request direct play when possible

  3. 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:

  1. Check CPU usage: docker stats jellyfin
  2. Reduce transcode bitrate in Admin → Playback
  3. Ask clients to prefer direct play
  4. Use Ethernet instead of Wi-Fi
  5. 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:

  1. Rename subtitles to match video: Movie.srt (not Movie.en.srt)
  2. Use SRT or ASS format (most compatible)
  3. In player settings, manually select subtitle stream
  4. Check Admin → Playback for subtitle encoder settings

Clients Can't Find Server on Network

Cause: Discovery disabled or firewall blocking

Solution:

  1. Ensure server is running: docker-compose ps
  2. Check firewall allows port 8096: sudo ufw allow 8096
  3. Manually add server in client settings: http://192.168.1.100:8096
  4. Use static IP for Pi to prevent discovery issues
  5. Check DLNA is enabled in Admin → Playback (for some devices)

Library Scan Takes Hours

Cause: Large library on slow storage or weak CPU

Solution:

  1. Use SSD instead of HDD if possible
  2. Exclude unwanted folders (behind the library → right-click → settings)
  3. Disable "Automatically add missing episodes" during initial scan
  4. 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.

Related Tools

Comments