Install Personal Media Server
I got tired of juggling Netflix, Disney+, and three other streaming subscriptions while still not finding what I wanted to watch. So I built a personal media server using Jellyfin and Docker, and honestly, it's been one of the most satisfying homelab projects I've completed. In this tutorial, I'll walk you through everything: hardware considerations, Docker Compose setup, library organization, and how to access your server remotely without exposing yourself to security nightmares.
Why Build Your Own Media Server?
Look, I'm not here to preach about "sticking it to big tech." The honest truth is this: a personal media server gives you control over your media collection, zero licensing drama, and the ability to stream anything from anywhere—your living room, your parents' house, or your office. You own your setup. No algorithm deciding what you should watch. No ads. No surprise subscription hikes.
The hardware bar is incredibly low in 2026. I'm running my media server on a refurbished Intel NUC from 2018 that cost me $120. It handles 4K transcoding for remote users and serves 8 concurrent streams without breaking a sweat. If you've got an old laptop, a Raspberry Pi 4, or a spare computer in your closet, you can start today.
Hardware and Network Requirements
Before diving into software, you need to think about your media library size and concurrent users. Here's my honest breakdown:
Minimum setup: Raspberry Pi 4 (8GB RAM), 2TB external drive, 1 Gbps home network. This works fine for a single user or couple streaming locally without transcoding.
Comfortable setup (what I run): Intel NUC or equivalent x86 box with 8GB+ RAM, 8TB+ storage (mix of SSD and HDD), 1 Gbps network. Handles 2-3 concurrent streams with transcoding.
Heavy-duty setup: Actual server hardware (used Lenovo ThinkCentre or Dell Optiplex), 16GB+ RAM, 20TB+ storage, 10 Gbps if possible. For families or shared homelabs with 5+ concurrent streams.
For storage, I prefer a mix: 2-4TB SSD for the OS and Jellyfin database, then 4-8TB HDD for actual media. Storage doesn't need to be fast—it needs to be reliable and cheap. Don't skimp on power supplies or cooling.
Installing Jellyfin with Docker Compose
I prefer Docker Compose for media servers because it keeps everything isolated, makes backups trivial, and lets me update Jellyfin without breaking anything else. Here's my production setup:
mkdir -p ~/media-server/{jellyfin,media}
cd ~/media-server
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
user: 1000:1000
ports:
- "8096:8096"
- "8920:8920"
environment:
- JELLYFIN_CACHE_DIR=/var/cache/jellyfin
- JELLYFIN_DATA_DIR=/var/lib/jellyfin
- JELLYFIN_LOG_DIR=/var/log/jellyfin
- JELLYFIN_CONFIG_DIR=/etc/jellyfin
volumes:
- ./jellyfin/config:/etc/jellyfin
- ./jellyfin/cache:/var/cache/jellyfin
- ./jellyfin/log:/var/log/jellyfin
- ./media/movies:/media/movies:ro
- ./media/tv:/media/tv:ro
- ./media/music:/media/music:ro
restart: unless-stopped
networks:
- media-net
devices:
- /dev/dri:/dev/dri
networks:
media-net:
driver: bridge
EOF
A few things I've learned the hard way:
User permissions matter. That `user: 1000:1000` line is crucial. Make sure your media files have matching ownership: `chown -R 1000:1000 ~/media-server/media`. Otherwise you'll get permission errors when Jellyfin tries to read your files.
GPU acceleration saves CPU. The `/dev/dri` device line passes your GPU to Docker. On Intel, this handles H.264 and H.265 transcoding efficiently. On NVIDIA, you'll need a different image (`jellyfin/jellyfin:latest-nvidia`), but it's worth it if you have lots of concurrent streams.
Read-only mounts. Notice the `:ro` flags on media volumes. This prevents Jellyfin from accidentally modifying your media files—a small safeguard that's saved me from mistakes.
Start the stack:
docker-compose up -d
docker-compose logs -f jellyfin
Wait 30 seconds for startup. You'll see it bind to port 8096. Navigate to `http://localhost:8096` and you'll hit the initial setup wizard.
Initial Configuration and Library Setup
The Jellyfin wizard is straightforward. Set your username, password, and preferred language. Skip the remote access setup for now—we'll come back to that securely.
The crucial part is adding your media libraries. In the web UI, go to Admin > Libraries > Add Media Library. Create separate libraries for:
• Movies: Point to `/media/movies`. Use the default naming scheme (this matters for metadata matching).
• TV Shows: Point to `/media/tv`. Jellyfin expects shows organized as `Show Name/Season 01/Episode.mkv`.
• Music: Point to `/media/music`. Artist/Album/Track structure works best.
File naming is critical. For movies, use `Movie Name (Year).mkv`. For TV, use `Show Name S01E01 - Episode Title.mkv`. Jellyfin's metadata scraper will find artwork, descriptions, and ratings automatically if you follow these conventions.
Remote Access with Reverse Proxy
Here's where most people mess up security. They open Jellyfin directly to the internet on port 8096, which is a vulnerability waiting to happen. Instead, I use Caddy as a reverse proxy with automatic HTTPS and Tailscale for encrypted remote access.
If you're using Tailscale (which I strongly recommend for homelabs), your Caddyfile looks like this:
media.{your-tailscale-domain} {
reverse_proxy http://jellyfin:8096 {
header_upstream Host {host}
header_upstream X-Real-IP {remote_host}
header_upstream X-Forwarded-For {remote_host}
header_upstream X-Forwarded-Proto https
}
}
Install Tailscale on your homelab machine and all your clients. Then you access Jellyfin via your Tailscale network—encrypted, no port forwarding, no firewall rules. It's elegant and secure.
If you absolutely need public internet access (not on Tailscale), use a domain you own, point it to Caddy with Let's Encrypt, and keep Jellyfin updated religiously. Even better: use Cloudflare Tunnel to avoid opening ports at all. But honestly, Tailscale is the move for homelabs.
Performance Tuning and Transcoding
By default, Jellyfin transcodes everything, which destroys CPU performance. I've optimized mine by being selective about when transcoding happens.
In Admin > Transcoding, I set maximum bitrate limits per quality tier. For remote users on slower connections, I transcode to 720p at 3 Mbps. For local network users, I disable transcoding entirely and just use direct play.
Create user profiles with different transcoding rules. My parents' account automatically limits to 1080p@5Mbps because their rural connection can't handle more. My account on the home network? Direct play everything, no transcoding overhead.
Also, I set temporary transcode storage to my SSD (`/var/cache/jellyfin`) instead of the HDD. This reduces latency when multiple users are transcoding simultaneously.
Backup Strategy
Your Jellyfin config lives in `./jellyfin/config`. Backup this directory weekly. If something breaks, you can restore your entire server—all libraries, users, playback state—in minutes. I use Restic with a daily automated backup to cold storage.
Your actual media files? Those are important but recoverable. I keep two copies: one on my local server, one on a networked backup drive using rsync. If the server fails, I've still got my media and can restore within an hour.
Next Steps and Expansion
Once you've got Jellyfin running smoothly, the natural next step is adding companion services. Many people add:
• Sonarr and Radarr for automated episode and movie management
• Prowlarr for indexer management
• SABnzbd or qBittorrent for downloading
These create a complete home media ecosystem, but that's a separate (complex) tutorial. For now, focus on getting Jellyfin stable and your library organized. That's 90% of the value.
If you're running this on a VPS instead of local hardware, scaling up becomes easier than you think. A RackNerd KVM VPS with 8GB RAM and 500GB storage can absolutely run a Jellyfin server for personal use—just budget for bandwidth costs if your friends are streaming from you constantly.
The key is starting somewhere. Your old laptop can become a media server. Your spare Pi 4 can handle this. Even a cheap VPS can work. Build it, organize your library, and enjoy streaming without the subscription treadmill.