The Complete Self-Hosted Media Stack 2026: From Request to Stream
You have seen the individual guides. Radarr here, Sonarr there, Jellyfin over there. But how does the entire system fit together? What talks to what? What is the exact sequence of events from "I want to watch Dune" to the movie playing on your TV?
This guide maps the complete architecture of a professional self-hosted media stack in 2026. Every tool, every connection, every data flow.
The Architecture Overview
User Request Flow:
User (phone/browser)
|
v
[Seerr] --- "I want Dune Part Two"
|
v
[Radarr] --- "Find and grab Dune Part Two"
|
v
[Prowlarr] --- "Search all indexers for releases"
|
v
[qBittorrent] --- "Download the best release"
|
v
[Radarr] --- "Import, rename, organize"
|
v
[Bazarr] --- "Download subtitles"
|
v
[Tdarr] --- "Optimize codec/size (optional)"
|
v
[Jellyfin] --- "Serve to any device"
|
v
User (TV/phone/browser) --- "Watching Dune Part Two"
Monitoring Layer:
[JellyWatch] --- Real-time sessions, push notifications
[Uptime Kuma] --- Service availability
[Jellystat] --- Historical analytics
Every Tool Explained
Layer 1: User Interface
Seerr (Media Requests)
What it does: Provides a Netflix-like interface where users search for movies and TV shows and submit requests.
Connects to:
- Jellyfin/Emby (user authentication, library status)
- Radarr (sends movie requests)
- Sonarr (sends TV show requests)
- TMDB (metadata and search)
Data flow: User searches TMDB via Seerr UI > clicks Request > Seerr sends the TMDB ID to Radarr or Sonarr > request status tracked until content is available.
Port: 5055
Layer 2: Media Management
Radarr (Movie Automation)
What it does: Monitors for movie releases, grabs them from indexers via download clients, imports and renames files into your library.
Connects to:
- Prowlarr (receives indexer configurations)
- qBittorrent/Transmission (sends download commands)
- Jellyfin (triggers library scan after import)
- Seerr (receives requests)
- Recyclarr/Profilarr (receives quality profile configurations)
Data flow: Receives request from Seerr (or manual add) > searches indexers via Prowlarr > sends .torrent to qBittorrent > monitors download progress > on completion: renames file, moves to library folder, notifies Jellyfin.
Port: 7878
Sonarr (TV Show Automation)
What it does: Same as Radarr but for TV series. Monitors for new episodes, grabs them automatically as they air.
Connects to: Same as Radarr.
Data flow: Identical to Radarr but operates on series/seasons/episodes instead of movies.
Port: 8989
Prowlarr (Indexer Management)
What it does: Centralizes all your torrent/usenet indexer configurations. Configure indexers once in Prowlarr, they sync to all connected apps.
Connects to:
- Radarr (pushes indexer configs)
- Sonarr (pushes indexer configs)
- External indexers (searches for releases)
Data flow: Radarr/Sonarr search requests are proxied through Prowlarr > Prowlarr queries all configured indexers > returns results to the requesting app.
Port: 9696
Layer 3: Download
qBittorrent (Download Client)
What it does: Downloads torrent files. Manages seeding, bandwidth, and queue priority.
Connects to:
- Radarr (receives download commands, reports status)
- Sonarr (receives download commands, reports status)
- VPN (optional, for privacy)
Data flow: Receives .torrent or magnet link from Radarr/Sonarr > downloads to /downloads/incomplete > moves to /downloads/complete on finish > Radarr/Sonarr detect completion and import.
Port: 8080 (WebUI)
Layer 4: Post-Processing
Bazarr (Subtitle Automation)
What it does: Automatically downloads subtitles for every movie and episode in your library from multiple providers.
Connects to:
- Radarr (reads movie library)
- Sonarr (reads TV library)
- OpenSubtitles, Addic7ed, Podnapisi (subtitle providers)
Data flow: Scans Radarr/Sonarr libraries > identifies missing subtitles > searches providers > downloads best match > places .srt file next to the video file.
Port: 6767
Tdarr (Library Transcoding - Optional)
What it does: Automatically transcodes your library to a target codec/format. Converts H.264 to H.265 to save storage, adds compatibility audio tracks, removes unwanted subtitle tracks.
Connects to:
- Your media folder (reads and writes files)
- Jellyfin (triggers library refresh after processing)
Data flow: Scans media folders > identifies files not matching target specs > transcodes using GPU/CPU > replaces original (or saves alongside) > notifies Jellyfin.
Port: 8265 (WebUI), 8266 (Server)
Layer 5: Media Server
Jellyfin (Streaming)
What it does: Organizes your media library with metadata, serves it to any device with transcoding when needed.
Connects to:
- Radarr/Sonarr (receives library scan triggers)
- All client devices (streams media)
- JellyWatch (provides API data for monitoring)
Data flow: Scans library folders > fetches metadata from TMDB/TVDB > serves content to clients > transcodes if client cannot Direct Play.
Port: 8096
Layer 6: Monitoring
JellyWatch (Mobile Admin)
What it does: Native Android app for real-time server monitoring, push notifications, and arr stack management.
Connects to:
- Jellyfin API (sessions, server health)
- Radarr API (download queue)
- Sonarr API (download queue)
- Seerr API (media requests)
Data flow: Polls Jellyfin API every 5-10 seconds > displays active sessions > sends push notifications for events > allows request approval/denial.
Uptime Kuma (Availability)
What it does: Checks if all services are responding. Sends alerts when something goes down.
Connects to: All services via HTTP health endpoints.
Port: 3001
Jellystat (Analytics)
What it does: Historical playback statistics, per-user analytics, library growth tracking.
Connects to: Jellyfin API (playback history).
Port: 3000
The Complete Docker Compose
services:
# === REVERSE PROXY ===
caddy:
image: caddy:2-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
restart: unless-stopped
# === MEDIA SERVER ===
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
network_mode: host
volumes:
- ./jellyfin/config:/config
- ./jellyfin/cache:/cache
- /mnt/media:/media:ro
devices:
- /dev/dri:/dev/dri
restart: unless-stopped
# === REQUESTS ===
seerr:
image: ghcr.io/seerr-team/seerr:latest
container_name: seerr
init: true
environment:
- TZ=Europe/Paris
volumes:
- ./seerr/config:/app/config
ports:
- 5055:5055
restart: unless-stopped
# === INDEXER MANAGEMENT ===
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
container_name: prowlarr
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Paris
volumes:
- ./prowlarr/config:/config
ports:
- 9696:9696
restart: unless-stopped
# === MOVIE AUTOMATION ===
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Paris
volumes:
- ./radarr/config:/config
- /mnt/media/movies:/movies
- /mnt/downloads:/downloads
ports:
- 7878:7878
restart: unless-stopped
# === TV AUTOMATION ===
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Paris
volumes:
- ./sonarr/config:/config
- /mnt/media/tv:/tv
- /mnt/downloads:/downloads
ports:
- 8989:8989
restart: unless-stopped
# === DOWNLOAD CLIENT ===
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Paris
- WEBUI_PORT=8080
volumes:
- ./qbittorrent/config:/config
- /mnt/downloads:/downloads
ports:
- 8080:8080
- 6881:6881
- 6881:6881/udp
restart: unless-stopped
# === SUBTITLES ===
bazarr:
image: lscr.io/linuxserver/bazarr:latest
container_name: bazarr
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Paris
volumes:
- ./bazarr/config:/config
- /mnt/media/movies:/movies
- /mnt/media/tv:/tv
ports:
- 6767:6767
restart: unless-stopped
# === LIBRARY TRANSCODING (OPTIONAL) ===
tdarr:
image: ghcr.io/haveagitgat/tdarr:latest
container_name: tdarr
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Paris
- serverIP=0.0.0.0
- serverPort=8266
- webUIPort=8265
- internalNode=true
- inContainer=true
- nodeName=InternalNode
volumes:
- ./tdarr/server:/app/server
- ./tdarr/configs:/app/configs
- ./tdarr/logs:/app/logs
- /mnt/media:/media
- /tmp/tdarr:/temp
ports:
- 8265:8265
- 8266:8266
devices:
- /dev/dri:/dev/dri
restart: unless-stopped
# === MONITORING ===
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
volumes:
- ./uptime-kuma/data:/app/data
ports:
- 3001:3001
restart: unless-stopped
volumes:
caddy_data:
The Connection Map (Setup Order)
- Prowlarr > Indexers - Add your torrent indexers
- Prowlarr > Radarr + Sonarr - Sync indexers to both apps
- Radarr + Sonarr > qBittorrent - Connect download client
- Radarr + Sonarr > Jellyfin - Trigger library scans on import
- Bazarr > Radarr + Sonarr - Read libraries for subtitle matching
- Seerr > Jellyfin + Radarr + Sonarr - Setup wizard connects all three
- JellyWatch > All services - Add each with URL and API key
The Data Flow: A Complete Example
- User opens Seerr on their phone, searches "Dune Part Two"
- Seerr queries TMDB, shows the movie with poster and description
- User taps Request. Seerr checks Jellyfin: not in library. Sends request to Radarr.
- Radarr receives the request, adds the movie as monitored
- Radarr searches via Prowlarr. Prowlarr queries all configured indexers simultaneously.
- Prowlarr returns results ranked by quality profile scores
- Radarr selects the best release based on custom format scoring
- Radarr sends the .torrent to qBittorrent with category "radarr"
- qBittorrent downloads the file to /downloads/incomplete, then moves to /downloads/complete
- Radarr detects completion, renames the file, hardlinks to /movies/
- Radarr notifies Jellyfin to scan the Movies library
- Bazarr detects the new movie, downloads English and French .srt files
- Tdarr detects the new file (if configured), checks codec, skips if already optimal
- Jellyfin scans, fetches TMDB metadata, downloads poster
- Seerr updates request status to "Available"
- JellyWatch sends a push notification: "Dune Part Two is now available"
- User opens Jellyfin on their TV, finds Dune Part Two
- Jellyfin serves the file via Direct Play
- JellyWatch shows the active session with codec details
Total time from request to watching: 10-30 minutes.
Storage Architecture
/mnt/
downloads/
incomplete/ <- qBittorrent active downloads
complete/
radarr/ <- Completed movie downloads (seeding)
sonarr/ <- Completed TV downloads (seeding)
media/
movies/ <- Radarr imports here (Jellyfin reads)
tv/ <- Sonarr imports here (Jellyfin reads)
music/ <- Manual or Lidarr
audiobooks/ <- Manual
Critical: /mnt/downloads and /mnt/media must be on the same filesystem for hardlinks to work.
Hardware Requirements by Stack Size
| Stack size | CPU | RAM | Storage (OS+Apps) | Storage (Media) |
|---|---|---|---|---|
| Solo user | Intel N100 (4 cores) | 8 GB | 120 GB NVMe | 2-4 TB HDD |
| Family (2-5 users) | Intel N100/N305 | 16 GB | 256 GB NVMe | 4-12 TB HDD |
| Shared server (5-15 users) | Intel i5 12th gen+ | 32 GB | 512 GB NVMe | 12-40 TB |
| Community (15+ users) | Intel i7/Xeon + dGPU | 64 GB | 1 TB NVMe | 40+ TB |
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Different filesystems for downloads and media | Hardlinks fail, storage doubles | Use same mount point |
| Wrong PUID/PGID across containers | Permission denied on import | Set same UID/GID everywhere |
| No Prowlarr (indexers in each app) | Configuration drift | Centralize in Prowlarr |
| No Jellyfin notification in Radarr/Sonarr | Library does not update after import | Add Connect > Jellyfin |
| Tdarr running during peak hours | Competes with active streams | Schedule off-peak |
| No monitoring | Problems discovered by users complaining | Deploy JellyWatch + Uptime Kuma |
FAQ
Do I need ALL of these tools? No. The minimum viable stack is: Jellyfin + Radarr + Sonarr + Prowlarr + qBittorrent. Everything else is optional enhancement.
How much does this cost? All software is free. Hardware cost: $130 (Intel N100 mini PC) + storage drives. Electricity: ~$15/year.
Can I add tools incrementally? Yes. Start with Jellyfin alone. Add Radarr/Sonarr when you want automation. Add Seerr when you share with others. Add monitoring when you want visibility.
Does this work on a NAS? Yes. Synology, TrueNAS, and Unraid all support Docker and can run this entire stack.
Your stack is deployed. Monitor it all from one app. Download JellyWatch on Google Play - Jellyfin sessions, Radarr/Sonarr queues, Seerr requests, and server health in your pocket.
On Emby? Download EmbyWatch on Google Play




Comments
No comments yet. Be the first to share your thoughts.
Leave a comment