Fransys

Tech blog — Architecture, Cloud & DevOps

BlogServicesContactAbout

Follow me

githubGitHublinkedinLinkedinmailMail

© 2026 Fransys • Fransys

Fransys

Categories

  • All posts
  • Tags
  • productivity10
  • nas10
  • ai8
  • security7
  • self-hosting7
  • linux6
  • claude-code6
  • neovim5
  • docker5
  • editor4
  • networking4
  • mcp3
  • vpn3
  • lua2
  • terminal2
nasdockerself-hostingmedia

Building a complete media center with Jellyfin and the *arr stack

Published on
February 17, 2026·6 min read
Avatar François GUERLEZFrançois GUERLEZ

Why self-host a media center?

It's gotten insane. Netflix, Disney+, Prime, Paramount+, Apple TV+... at some point you just say: why am I paying for six subscriptions to watch two shows a month? And that's not even the worst part. Every single thing you watch gets tracked. Every pause. Every search. Monetized. Creepy.

So you self-host instead. You take back control. No monthly bill creeping up, no content vanishing overnight because some licensing deal expired, and you can actually share with your family without hitting stream limits. My partner watches a show, the kids watch cartoons, I watch docs - all simultaneously. Zero restrictions.

I built this on my TerraMaster F4-424 running Debian 13. Complete pipeline from content discovery to playback on any screen in the house. Took me a solid day to set up initially, honestly, but now it just runs. I forget it's there most of the time.

Jellyfin: the heart

Jellyfin is an open-source fork of Emby. Nothing locked behind a paywall. Handles movies, shows, music, photos, even live TV if you plug in a tuner.

Here's the thing: Jellyfin runs in host network mode, not bridge mode like my other Docker services. Why? The DLNA/SSDP discovery protocol needs multicast access, and Docker's bridge network doesn't play nicely with that. Learned that the hard way when I first tried to connect an old Freebox as a DLNA client and got nothing.

# docker-compose.yml - Jellyfin
services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    network_mode: host
    environment:
      - JELLYFIN_PublishedServerUrl=https://jellyfin.home.lan
    volumes:
      - /mnt/data/apps/jellyfin/config:/config
      - /mnt/data/apps/jellyfin/cache:/cache
      - /mnt/data/media/movies:/data/movies
      - /mnt/data/media/tv:/data/tv
      - /mnt/data/media/music:/data/music
    restart: unless-stopped

Web interface lives on port 8096. And here's the good news: Jellyfin supports hardware transcoding through Intel Quick Sync (QSV) via the N95's iGPU. Real-time 4K HEVC transcoding without melting the CPU. First time I tested it I couldn't believe it worked.

Authentication? Jellyfin integrated with my Authelia instance via OIDC/SSO. Everyone logs in with centralized credentials. Done.

The *arr ecosystem: where the magic happens

The real power is the *arr tools. A bunch of apps that automate everything: discovering content, downloading it, organizing it. Each one does one thing well.

Prowlarr: the indexer hub

Prowlarr centralizes your indexer configuration. Instead of setting up the same sources in both Sonarr and Radarr separately, you declare them once here and Prowlarr syncs them everywhere. Port 9696.

Sonarr and Radarr: set it and forget it

Sonarr handles TV. Radarr handles movies. Same workflow: add a title, pick your quality (1080p, 4K, whatever), and walk away. It watches RSS feeds, grabs new episodes or the best available versions, renames everything according to a naming scheme Jellyfin understands. It's honestly magic.

  • Sonarr: port 8989
  • Radarr: port 7878

I spent hours the first time tweaking quality profiles. After that? Nothing. Never touched it again.

Bazarr: subtitles done right

Bazarr watches your Sonarr and Radarr libraries and automatically downloads missing subtitles. Multiple sources, picks the best match. Port 6767. Works flawlessly.

qBittorrent: the download engine

This is the client that Sonarr and Radarr use to actually grab files. Web UI on port 8080, BitTorrent protocol on 6881.

FlareSolverr: CloudFlare bypass

Some indexers hide behind CloudFlare protection. FlareSolverr acts as a proxy to solve those challenges. Port 8191. Without it, certain indexers just block you.

Jellyseerr: the normal person interface

Then there's Jellyseerr. It's the UI for people who don't want to care about the technical side. Your wife wants to watch a show? She searches in Jellyseerr, clicks "Request", and that's it - Sonarr or Radarr handle it behind the scenes. Nobody needs to understand the machinery. Port 5055, integrated with Authelia via OIDC.

Network and storage architecture

All the *arr services run on an isolated Docker network arr_net. Only Jellyfin (host network) and Jellyseerr (reverse proxy) are exposed to the LAN.

# docker-compose.yml - Sonarr + Radarr
services:
  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    container_name: sonarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Paris
    volumes:
      - /mnt/data/apps/arr/sonarr:/config
      - /mnt/data/media/tv:/tv
      - /mnt/data/downloads:/downloads
    networks:
      - arr_net
    restart: unless-stopped

  radarr:
    image: lscr.io/linuxserver/radarr:latest
    container_name: radarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Paris
    volumes:
      - /mnt/data/apps/arr/radarr:/config
      - /mnt/data/media/movies:/movies
      - /mnt/data/downloads:/downloads
    networks:
      - arr_net
    restart: unless-stopped

networks:
  arr_net:
    name: arr_net

LinuxServer images use PUID/PGID (here 1000:1000) so file permissions stay sane. Essential, otherwise you end up with weird permission conflicts.

Directory structure (don't mess this up)

This is where people screw up. The folder layout matters for hardlinks:

/mnt/data/
├── apps/arr/
│   ├── sonarr/          # Sonarr config
│   ├── radarr/          # Radarr config
│   ├── prowlarr/        # Prowlarr config
│   ├── bazarr/          # Bazarr config
│   ├── qbittorrent/     # qBittorrent config
│   └── jellyseerr/      # Jellyseerr config
├── downloads/
│   ├── complete/        # Finished downloads
│   └── incomplete/      # In-progress
└── media/
    ├── movies/          # Movies organized by Radarr
    └── tv/              # Shows organized by Sonarr

Critical part: downloads and media live on the same filesystem. That allows Sonarr and Radarr to hardlink instead of copy. Instant, zero disk space wasted. I didn't know this at first - ended up duplicating everything like an idiot before I fixed it.

The complete workflow

When someone requests a movie:

  1. Jellyseerr: someone searches and requests a movie
  2. Radarr: gets the request, queries indexers through Prowlarr
  3. Prowlarr: sends the search to configured sources
  4. qBittorrent: downloads to /mnt/data/downloads/complete
  5. Radarr: detects completion, renames it, creates hardlink in /mnt/data/media/movies
  6. Bazarr: detects new movie, downloads subtitles
  7. Jellyfin: scans library, movie is now available

From the user's perspective? Clicked a button, and a few minutes later it's in Jellyfin. All the machinery is invisible.

Summary

Setup takes time the first time around (burned a whole day getting everything right), but then it just works. Ansible handles redeploys. The *arr stack plus Jellyfin gives you streaming service quality without subscriptions or restrictions. The N95 handles transcoding effortlessly. The isolated arr_net keeps things secure and clean. Honestly, it's my favorite NAS deployment.


Debian NAS from scratch series — This article is part of a complete series on building a Debian NAS.

Previous: Secure remote access to your NAS with Tailscale | Next: Managing photos with self-hosted Immich

Previous post

← Terminal, Git and global search integrated in Neovim

Next post

Optimizing your Claude Code configuration for development→
← Back to blog

Table of Contents

  • Why self-host a media center?
  • Jellyfin: the heart
  • The *arr ecosystem: where the magic happens
  • Prowlarr: the indexer hub
  • Sonarr and Radarr: set it and forget it
  • Bazarr: subtitles done right
  • qBittorrent: the download engine
  • FlareSolverr: CloudFlare bypass
  • Jellyseerr: the normal person interface
  • Network and storage architecture
  • Directory structure (don't mess this up)
  • The complete workflow
  • Summary