One-command self-hosted media server. Request a movie or TV show and it's automatically downloaded, organized, subtitled, and ready to stream — like running your own Netflix.
bash <(curl -fsSL https://raw.githubusercontent.com/unbalancedparentheses/media-server/main/install.sh)
You ──> Jellyseerr (request) ──> Sonarr / Radarr ──> Prowlarr ──> Indexers
│
qBittorrent / SABnzbd
(through VPN tunnel)
│
download completes
├── Bazarr (subtitles)
└── Tdarr (transcode to H.265)
│
Jellyfin (stream it)
│
Janitorr (clean up unwatched)
- Request a movie or show through Jellyseerr (Netflix-like browse and request UI)
- Sonarr (TV/anime) or Radarr (movies) searches indexers via Prowlarr
- Best match is sent to qBittorrent (through VPN) or SABnzbd (Usenet)
- File is downloaded, renamed, and added to your Jellyfin library
- Bazarr fetches subtitles automatically (English + Spanish by default)
- Tdarr transcodes to H.265 in the background to save ~40-50% storage
- Janitorr removes content nobody has watched after a grace period
bash <(curl -fsSL https://raw.githubusercontent.com/unbalancedparentheses/media-server/main/install.sh)
Clones to ~/media-server, prompts for credentials, and runs full setup.
git clone https://github.com/unbalancedparentheses/media-server.git
cd media-server
cp config.toml.example config.toml # optional — setup creates one if missing
./setup.shFully idempotent — safe to re-run at any time.
- Installs Docker + dependencies
- Prompts for Jellyfin and qBittorrent credentials (or generates them with
--yes) - Creates
~/media/directory structure (libraries, downloads, configs) - Starts 25+ containers via Docker Compose
- Wires every service together (API keys, download clients, indexers, subtitles)
- Sets authentication on all services (Jellyfin credentials shared across the stack)
- Adds
.media.localdomains to/etc/hosts - Runs 90+ verification checks
./setup.sh # Full setup (interactive)
./setup.sh --yes # Full setup (non-interactive, generates passwords)
./setup.sh --test # Run verification checks only
./setup.sh --update # Pull latest images + restart
./setup.sh --backup # Backup all service configs
./setup.sh --restore <file> # Restore from backup
./setup.sh --preflight # Check prerequisites + config
./setup.sh --check-config # Validate config.toml only
./setup.sh --dry-run # Preview actions without changing anything| Service | What it does |
|---|---|
| Jellyfin | Stream your library — browser, iOS, Android, Apple TV, Fire TV, Roku, Chromecast |
| Jellyseerr | Request movies and shows — browse trending, search, track requests |
| Service | What it does |
|---|---|
| Sonarr | Automatically downloads and organizes TV shows |
| Sonarr Anime | Dedicated instance with anime indexers (Nyaa, SubsPlease, Mikan, Bangumi) |
| Radarr | Automatically downloads and organizes movies |
| Bazarr | Automatic subtitle downloads for everything |
| Prowlarr | Manages all your indexers in one place, syncs to Sonarr/Radarr |
| Service | What it does |
|---|---|
| qBittorrent | Torrent client, routed through VPN |
| SABnzbd | Usenet client (optional — requires a paid provider) |
| Gluetun | VPN tunnel for torrent traffic with kill switch (optional) |
| FlareSolverr | Bypasses Cloudflare on protected indexers |
| Unpackerr | Extracts compressed downloads so Sonarr/Radarr can import them |
| Service | What it does |
|---|---|
| Recyclarr | Syncs TRaSH Guide quality profiles to Sonarr/Radarr weekly |
| Janitorr | Removes unwatched content after a configurable grace period (starts in dry-run mode) |
| Tdarr | Transcodes media to H.265/HEVC to save storage |
| Service | What it does |
|---|---|
| Navidrome | Music streaming (works with DSub, Symfonium, etc.) |
| Lidarr | Music automation — monitors artists and downloads releases |
| Immich | Self-hosted Google Photos replacement with face/object recognition |
| Nginx | Reverse proxy — .media.local domains + landing page with live widgets |
| Dozzle | Live Docker log viewer (SSE streaming) |
| Beszel | System monitoring (CPU, RAM, disk, per-container) |
| Scrutiny | Hard drive S.M.A.R.T. health monitoring |
| Uptime Kuma | Service uptime monitoring with notifications |
| Tailscale | Mesh VPN for remote access (optional) |
Edit config.toml before running setup, or let setup prompt you interactively. See config.toml.example for all options.
[jellyfin]
username = "admin"
password = "changeme"
[qbittorrent]
username = "admin"
password = "changeme"Setup prompts for real passwords on first run. With --yes, secure random passwords are generated automatically. Jellyfin credentials are shared across most services (Sonarr, Radarr, Prowlarr, Bazarr, SABnzbd, Navidrome, Immich).
[quality]
sonarr_profile = "WEB-1080p"
sonarr_anime_profile = "Remux-1080p - Anime"
radarr_profile = "HD Bluray + WEB"TRaSH Guide profiles synced by Recyclarr. Defaults: 1080p web for TV, remux for anime, HD bluray for movies.
[subtitles]
languages = ["en", "es"]
providers = ["opensubtitlescom", "podnapisi", "yifysubtitles"]English always downloaded, additional languages when available. Nine providers pre-configured — some need free accounts (OpenSubtitles, Addic7ed), configurable in the Bazarr UI after setup.
Pre-configured with public torrent indexers and anime indexers:
[[indexers]]
name = "1337x"
definitionName = "1337x"
enable = true
flaresolverr = true # needs FlareSolverr for Cloudflare bypass
[[indexers]]
name = "Nyaa.si"
definitionName = "nyaasi"
enable = true
anime = true # routes to Sonarr Anime onlyIncluded by default: 1337x, EZTV, The Pirate Bay, YTS, Knaben, LimeTorrents, BitSearch, MegaPeer, Nyaa.si, SubsPlease, Mikan, Bangumi Moe. Add private trackers or usenet indexers as needed.
[vpn]
enable = false
provider = "mullvad"
type = "wireguard"
wireguard_private_key = ""
wireguard_addresses = ""
server_countries = "Switzerland"Routes torrent traffic through Gluetun. 30+ providers supported (Mullvad, ProtonVPN, NordVPN, Surfshark, etc.). Kill switch built in. Optional — disabled by default.
[[usenet_providers]]
name = "usenet"
enable = false
host = ""
port = 563
ssl = true
username = ""
password = ""
connections = 20Optional. Faster and more private than torrents, but requires a paid Usenet provider (Newshosting, Eweka, Frugal Usenet, etc.).
All services are available at http://<service>.media.local after setup.
| Service | URL |
|---|---|
| Landing page | http://media.local |
| Jellyfin | http://jellyfin.media.local |
| Jellyseerr | http://jellyseerr.media.local |
| Sonarr | http://sonarr.media.local |
| Sonarr Anime | http://sonarr-anime.media.local |
| Radarr | http://radarr.media.local |
| Prowlarr | http://prowlarr.media.local |
| Bazarr | http://bazarr.media.local |
| qBittorrent | http://qbittorrent.media.local |
| SABnzbd | http://sabnzbd.media.local |
| Lidarr | http://lidarr.media.local |
| Navidrome | http://navidrome.media.local |
| Immich | http://immich.media.local |
| Tdarr | http://tdarr.media.local |
For remote access, Tailscale provides mesh VPN with automatic HTTPS. Share with family/friends by inviting them to your tailnet.
~/media/
├── movies/ # Radarr
├── tv/ # Sonarr
├── anime/ # Sonarr Anime
├── music/ # Lidarr / Navidrome
├── photos/ # Immich
├── downloads/
│ ├── torrents/
│ └── usenet/
├── config/ # Per-service config directories
├── transcode_cache/ # Tdarr working directory
├── leaving-soon/ # Janitorr staging area
└── backups/ # Auto-pruned, keeps last 10
Some things you could add alongside this stack:
- Kavita — ebooks, comics, manga
- Readarr — book/audiobook automation
- Audiobookshelf — audiobook and podcast server
- Mylar3 — comic book automation
- TubeArchivist — YouTube archive
- Autobrr — IRC/RSS automation for private trackers