Create Personal Media Server

Create Personal Media Server

I stopped paying $15/month for streaming services the moment I realized I could host my entire media collection—movies, TV shows, music, photos—on hardware I already owned. A personal media server gives you complete control: no forced updates, no content licensing nonsense, no algorithmic recommendations you didn't ask for. You own the data, you own the experience. This guide walks you through building one with Jellyfin, Docker, and either local storage or a cheap VPS for remote access.

Why Self-Host Media Instead of Streaming?

The streaming economics are broken. Every service you subscribe to costs money, but none of them guarantee the content stays available. I had a film disappear from a platform mid-watchthrough because of licensing disputes. That won't happen on your own server.

Jellyfin is the open-source answer here. It's Plex's free cousin, with zero ads, no cloud account requirements, and full control over your library. Alternatively, you can run Plex if you prefer its polish and sharing features. Both work great in Docker, which is how I prefer to deploy anything on my homelab these days.

You'll also save money on storage. A 4TB drive costs $60–80. That's cheaper than two months of premium streaming services, and it's *yours* forever.

Hardware Requirements: Realistic Numbers

You don't need much. Here's what I recommend:

I'm running mine on a refurbished Lenovo ThinkCentre micro PC with an i5, 8GB RAM, and an external 4TB drive. Total cost: $150. It's been rock-solid for 18 months.

Tip: If you don't have old hardware lying around, check eBay or local used-computer shops. Refurbished office PCs are cheap and reliable. Avoid anything older than 5 years—power draw becomes an issue.

Setting Up Jellyfin with Docker

Docker is the easiest way to deploy Jellyfin. No dependency hell, no version conflicts. I'll walk you through a Docker Compose setup that includes Jellyfin, automatic media organization with Radarr (for movies) and Sonarr (for TV), and Prowlarr to manage your indexers.

First, create a directory structure:

mkdir -p ~/media-server/{jellyfin,downloads,movies,tv,music}
cd ~/media-server

Next, create a Docker Compose file that pulls everything together:

version: '3.8'

services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    hostname: jellyfin
    user: "1000:1000"
    ports:
      - "8096:8096"
    volumes:
      - ./jellyfin/config:/config
      - ./jellyfin/cache:/cache
      - ./movies:/media/movies:ro
      - ./tv:/media/tv:ro
      - ./music:/media/music:ro
    restart: unless-stopped
    environment:
      - JELLYFIN_DATA_DIR=/config
      - JELLYFIN_CACHE_DIR=/cache

  radarr:
    image: linuxserver/radarr:latest
    container_name: radarr
    hostname: radarr
    ports:
      - "7878:7878"
    volumes:
      - ./radarr/config:/config
      - ./downloads:/downloads
      - ./movies:/movies
    restart: unless-stopped
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC

  sonarr:
    image: linuxserver/sonarr:latest
    container_name: sonarr
    hostname: sonarr
    ports:
      - "8989:8989"
    volumes:
      - ./sonarr/config:/config
      - ./downloads:/downloads
      - ./tv:/tv
    restart: unless-stopped
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC

  prowlarr:
    image: linuxserver/prowlarr:latest
    container_name: prowlarr
    hostname: prowlarr
    ports:
      - "9696:9696"
    volumes:
      - ./prowlarr/config:/config
    restart: unless-stopped
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=UTC

Save that as `docker-compose.yml` in your `~/media-server` directory. Then spin everything up:

docker-compose up -d

Jellyfin will be available at `http://localhost:8096`. Go there, create an admin account, and point it to your media folders. Radarr (7878), Sonarr (8989), and Prowlarr (9696) need individual setup—that's out of scope here, but the web UIs walk you through it.

Watch out: Make sure the user ID (PUID=1000) matches your actual user on the host. Run `id` to check. If it doesn't match, you'll hit permission issues with mounted volumes.

Storage Strategy: Local vs. External

I prefer external USB 3.0 drives over internal SATA because they're portable and easier to swap if one fails. A 4TB WD Red Pro is $80 and rated for NAS duty. Two of them in RAID 1 (mirrored) costs $160 but gives you real redundancy.

For *movie* content specifically, I don't need RAID because files are easily re-obtained. For *personal video archives*—old family footage, recordings of my kids—I run two drives. One as the live library, one as a nightly backup.

To mount an external drive on Linux, first identify it:

lsblk

Find your drive (e.g., `/dev/sdb1`), then create a mount point and add it to `/etc/fstab`:

sudo mkdir -p /mnt/media
sudo mount /dev/sdb1 /mnt/media

Then edit `/etc/fstab` to make it permanent. Get the UUID first:

sudo blkid /dev/sdb1

Add a line like this to `/etc/fstab`:

UUID=your-uuid-here /mnt/media ext4 defaults,nofail 0 2

Reboot to confirm it mounts automatically.

Remote Access with Jellyfin and Tailscale

The tricky part: accessing your media server from outside your home. Jellyfin supports UPnP and port forwarding, but I hate exposing services directly to the internet. Instead, I use Tailscale—a mesh VPN that's free, fast, and requires zero port forwarding.

Install Tailscale on your server:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

It'll give you a login URL. Authenticate, then check your Tailscale IP:

tailscale ip -4

On your phone or laptop, install Tailscale, authenticate with the same account, and you can reach Jellyfin at `http://your-tailscale-ip:8096`. No port forwarding. No exposed services. Just a private tunnel to your media server. This is how I've done it for two years—bulletproof.

Adding Your Media Library

The easiest path: download movies and TV shows you own legally. Sonarr and Radarr automate this if you connect them to indexers via Prowlarr, but that's a rabbit hole. For this guide, assume you're adding files you already have or own licenses to.

Copy them into your media folders:

cp /path/to/your/movies/* ~/media-server/movies/
cp /path/to/your/shows/* ~/media-server/tv/

Jellyfin will auto-scan and index these within a few minutes. Naming matters—use standard formats like `Movie Title (Year).mkv` for movies and `Show Name/Season 01/Episode 01 - Title.mkv` for TV.

Transcoding and Performance

By default, Jellyfin streams your files as-is. If your internet is slow or you're on mobile, that's painful. Transcoding converts the file in real-time to a lower bitrate.

In Jellyfin's Settings → Playback, enable "Allow Transcoding" and set a default bitrate for remote access (I use 3 Mbps for mobile, 8 Mbps for home). On an i5 or better, you can handle 2–3 simultaneous transcodes comfortably.

Transcoding eats CPU hard. If you plan heavy use, consider disabling it or getting better hardware. My i5 maxes out at about 40% utilization during a single transcode—plenty of headroom.

Backing Up Your Configuration

Jellyfin stores its database and settings in the `config` folder. Back it up weekly:

tar -czf ~/jellyfin-backup-$(date +%Y%m%d).tar.gz ~/media-server/jellyfin/config/

Push it to a cloud service (Backblaze, Wasabi, or Nextcloud). The entire config directory is maybe 100MB—cheap to backup and irreplaceable if something goes wrong.

Next Steps

Once your basic server is running, consider adding:

A personal media server puts you back in control of what you watch, when you watch it, and who sees it. It's the antidote to the streaming-service treadmill.

```