A self-hosted media ecosystem that combines media management, streaming, AI-powered recommendations, and VPN.
This stack includes:
- VPN: For secure and private media downloading
- Radarr: For movie management
- Sonarr: For TV show management
- Bazarr: For subtitle management
- Prowlarr: A torrent indexer manager for Radarr/Sonarr
- qBittorrent: Torrent client for downloading media
- FlareSolverr: Cloudflare proxy for indexers
- Jellyseerr: To manage media requests
- Jellyfin: Open-source media streamer
- Recommendarr: For AI-powered movie and show recommendations
- Glance: Dashboard with service bookmarks and widgets
- Beszel: System monitoring dashboard
- Portainer: Docker container management UI
- Docker version 28.0.1 or later
- Docker compose version v2.33.1 or later
- Older versions may work, but they have not been tested.
⚠️ Warning for ARMv7 Users:
Jellyseerr v2.0.x introduces breaking changes, dropping support for the ARMv7 container image. However, ARM64 support remains available. You will not be able to run Jellyseerr in ARMv7 CPU.
There are three ways to deploy this stack:
- With a VPN (Recommended)
- Without a VPN
- With Recommendarr (An optional tool for AI-generated movie and show recommendations)
NOTE: If you are installing this stack without a VPN, you must use the
no-vpnprofile.
This requirement prevents accidental or unintentional deployment of media-stack without VPN.Running the
docker composecommand without a profile will not deploy anything.Check the installation steps below.
Before deploying the stack, you must first create a Docker network:
docker network create --subnet 172.20.0.0/16 mynetwork
# Update the CIDR range based on your available IP rangeWhen VPN is enabled, qBittorrent and Prowlarr will run behind the VPN for added privacy.
By default, NordVPN is used in docker-compose.yml, but you can switch to:
- ExpressVPN
- SurfShark
- ProtonVPN
- Custom OpenVPN
- WireGuard VPN
All providers use the OpenVPN protocol.
➡️ Full list of supported VPN providers: VPN Providers
Refer to your VPN provider's documentation to generate an OpenVPN username and password.
For setup instructions, check:
➡️ Gluetun VPN Setup Guide
By default, VPN is disabled in docker-compose.yml. To enable it, simply comment/uncomment the required lines in the file.
The docker-compose.yml file includes clear instructions in the comments to guide you through the process.
Once updated, follow the steps below to deploy the stack with VPN.
VPN_SERVICE_PROVIDER=nordvpn OPENVPN_USER=openvpn-username OPENVPN_PASSWORD=openvpn-password SERVER_COUNTRIES=Switzerland RADARR_STATIC_CONTAINER_IP=radarr-container-static-ip SONARR_STATIC_CONTAINER_IP=sonarr-container-static-ip docker compose --profile vpn up -d
# OPTIONAL: Use Nginx as a reverse proxy
# docker compose -f docker-compose-nginx.yml up -dA static container IP address is needed when Prowlarr is behind a VPN.
Since Prowlarr can only communicate with Radarr and Sonarr using their container IP addresses,
these must be manually assigned to avoid connection issues when containers restart.
Use the following environment variables to set static IPs:
RADARR_STATIC_CONTAINER_IPSONARR_STATIC_CONTAINER_IP
🚨 Warning: Deploying without a VPN is highly discouraged as it may expose your IP address when torrenting media.
To proceed without VPN, run the following command:
docker compose --profile no-vpn up -d
# OPTIONAL: Use Nginx as a reverse proxy
# docker compose -f docker-compose-nginx.yml up -dRecommendarr is a web application that uses AI to generate personalized TV show and movie recommendations based on your:
- Sonarr library
- Radarr library
- Jellyfin watchlist and library
- Trakt watchlist (Optional)
Run the following command based on your setup:
COMPOSE_PROFILES=vpn,recommendarr docker compose up -d # With VPN
# COMPOSE_PROFILES=no-vpn,recommendarr docker compose up -d # Without VPN- Open qBitTorrent at http://localhost:5080. Default username is
admin. Temporary password can be collected from container logdocker logs qbittorrent - Go to Tools --> Options --> WebUI --> Change password
- Configure download paths:
- Tools --> Options --> Downloads --> Default Save Path:
/data/torrents - You may also want to set Category paths for better organization
- Tools --> Options --> Downloads --> Default Save Path:
- Open Radarr at http://localhost:7878
- Settings --> Media Management --> Check mark "Movies deleted from disk are automatically unmonitored in Radarr" under File management section --> Save
- Settings --> Media Management --> Scroll to bottom --> Add Root Folder --> Browse to /data/media/movies --> OK
- Settings --> Download clients --> qBittorrent --> Add Host (qbittorrent) and port (5080) --> Username and password --> Test --> Save Note: If VPN is enabled, then qbittorrent is reachable on vpn's service name. In this case use
vpnin Host field. - Settings --> General --> Enable advance setting --> Select Authentication and add username and password
- Indexer will get automatically added during configuration of Prowlarr. See 'Configure Prowlarr' section.
Sonarr can also be configured in similar way.
Bazarr is a companion application to Radarr and Sonarr that manages and downloads subtitles.
- Open Bazarr at http://localhost:6767
- Settings --> Languages --> Add your preferred languages for subtitles
- Settings --> Providers --> Add subtitle providers (OpenSubtitles, Subscene, etc.)
- Settings --> Radarr --> Enable and configure:
- Address: http://radarr:7878
- API Key: (Get from Radarr Settings --> General)
- Test and Save
- Settings --> Sonarr --> Enable and configure:
- Address: http://sonarr:8989
- API Key: (Get from Sonarr Settings --> General)
- Test and Save
- Bazarr will now automatically download subtitles for your movies and TV shows
Add a movie (After Prowlarr is configured)
- Movies --> Search for a movie --> Add Root folder (/data/media/movies) --> Quality profile --> Add movie
- All queued movies download can be checked here, Activities --> Queue
- Go to qBittorrent (http://localhost:5080) and see if movie is getting downloaded (After movie is queued. This depends on availability of movie in indexers configured in Prowlarr.)
- Open Jellyfin at http://localhost:8096
- When you access the jellyfin for first time using browser, A guided configuration will guide you to configure jellyfin. Just follow the guide.
- Add media library folders:
- For Movies: /data/media (Jellyfin has this path mapped)
- Content type: Movies
- Repeat for TV Shows if needed
FlareSolverr is a proxy server to bypass Cloudflare protection on indexers. It's automatically configured to work with Prowlarr.
- FlareSolverr runs at http://localhost:8191
- In Prowlarr, when adding an indexer that requires FlareSolverr:
- Go to Settings --> Indexers --> Add Indexer
- For indexers with Cloudflare protection, look for FlareSolverr option
- Set FlareSolverr URL to: http://flaresolverr:8191
- Test and Save
- No additional configuration needed, it works automatically once linked in Prowlarr
Here's a quick reference for all services in the stack:
| Service | Port | URL | Description |
|---|---|---|---|
| Glance | 8080 | http://localhost:8080 | Dashboard with service bookmarks |
| Dozzle | 8081 | http://localhost:8081 | Docker log viewer |
| Code Server | 8443 | https://localhost:8443 | Web-based VS Code |
| Jellyfin | 8096 | http://localhost:8096 | Media streaming server |
| Radarr | 7878 | http://localhost:7878 | Movie management |
| Sonarr | 8989 | http://localhost:8989 | TV show management |
| Bazarr | 6767 | http://localhost:6767 | Subtitle management |
| Prowlarr | 9696 | http://localhost:9696 | Indexer manager |
| qBittorrent | 5080 | http://localhost:5080 | Torrent client |
| Jellyseerr | 5055 | http://localhost:5055 | Media requests |
| Recommendarr | 3000 | http://localhost:3000 | AI recommendations |
| FlareSolverr | 8191 | http://localhost:8191 | Cloudflare proxy |
| Beszel | 8090 | http://localhost:8090 | System monitoring |
| Portainer | 9000 | http://localhost:9000 | Container management |
- Open Jellyfin at http://localhost:5055
- When you access the jellyseerr for first time using browser, A guided configuration will guide you to configure jellyseerr. Just follow the guide and provide the required details about sonarr and Radarr.
- Follow the Overseerr document (Jellyseerr is fork of overseerr) for detailed setup - https://docs.overseerr.dev/
- Open Prowlarr at http://localhost:9696
- Settings --> General --> Authentications --> Select Authentication and add username and password
- Add Indexers, Indexers --> Add Indexer --> Search for indexer --> Choose base URL --> Test and Save
- Add application, Settings --> Apps --> Add application --> Choose Radarr --> Prowlarr server (http://prowlarr:9696) --> Radarr server (http://radarr:7878) --> API Key --> Test and Save
- Add application, Settings --> Apps --> Add application --> Choose Sonarr --> Prowlarr server (http://prowlarr:9696) --> Sonarr server (http://sonarr:8989) --> API Key --> Test and Save
- This will add indexers in respective apps automatically.
Note: If VPN is enabled, then Prowlarr will not be able to reach radarr and sonarr with localhost or container service name. In that case use static IP for sonarr and radarr in radarr/sonarr server field (for e.g. http://172.19.0.5:8989). Prowlar will also be not reachable with its container/service name. Use http://vpn:9696 instead in prowlar server field.
Recommendarr is an AI based movies/tvshows recommendation tool. To use this you will need any OpenAI API URL and API key with atleast one LLM model running. You can host your own OpenAI server with AI model using ollama or LM Studio. Or you can check https://openrouter.ai for limited-free LLMs.
- Open Recommendarr at http://localhost:3000
- Login with default username
adminand password1234 - Settings --> Account --> Change Password and change your admin password
- Settings --> AI service --> API URL (Add OpenAI server API URL) --> API Key (Add OpenAPI server API key) --> Fetch available models --> Set Max tokens (best to keep it under 2000) --> Set Temperature (Best to keep at 0.8)
- Settings --> Sonarr --> Sonarr URL (http://sonarr:8989) --> API Keys (Sonarr API Key) --> Test Connection --> Save Sonarr setting
- Settings --> Radarr --> Radarr URL (http://radarr:7878) --> API Keys (Radarr API Key) --> Test Connection --> Save Radarr setting
- Settings --> Jellyfin --> Jellyfin URL (http://jellyfin:8096) --> API Keys (Jellyfin API Key) --> User ID (Add your jellyfin user id) --> Test Connection --> Save Jellyfin settings
- Test recommendarr: Recommendations --> Choose LLM Model from drop down list --> Enable Jellyfin Watch History toggle --> Select language --> Choose genres --> Discover recommendations
- You should be able to see recommendations based on your Jellyfin watch history
Glance is a dashboard that provides a unified view of all your services with bookmarks, RSS feeds, calendar, and other widgets.
- Open Glance at http://localhost:8080
- The dashboard comes pre-configured with two pages:
- Home: Widgets for calendar, RSS feeds, Hacker News, Reddit, weather, and more
- Services: Bookmarks to all media stack services organized by category
- To customize the dashboard, edit
config/glance/glance.yml - Update service URLs in the configuration file if accessing from a different IP or domain
- Restart the Glance container after making changes:
docker compose restart glance
Beszel is a lightweight system monitoring dashboard for tracking server metrics and Docker containers.
- Open Beszel at http://localhost:8090
- On first access, create an admin account
- The Beszel agent is automatically configured to monitor the host system
- You can view CPU, memory, disk usage, and Docker container statistics
- Add additional servers by deploying the Beszel agent on other machines
Portainer is a container management UI that makes it easy to manage Docker containers, images, networks, and volumes.
- Open Portainer at http://localhost:9000
- On first access, create an admin account (you have 5 minutes to do this)
- Choose "Get Started" to connect to the local Docker environment
- You can now manage all your containers through the Portainer UI
- Features include:
- Start/stop/restart containers
- View container logs and stats
- Manage images, volumes, and networks
- Deploy new stacks via docker-compose
- Execute commands inside containers
- Get inside Nginx container
cd /etc/nginx/conf.d- Add proxies for all tools.
docker cp nginx.conf nginx:/etc/nginx/conf.d/default.conf && docker exec -it nginx nginx -s reload
- Close ports of other tools in firewall/security groups except port 80 and 443.
- Open port 80 and 443.
- Get inside Nginx container and install certbot and certbot-nginx
apk add certbot certbot-nginx - Add URL in server block. e.g.
server_name localhost mediastack.example.com;in /etc/nginx/conf.d/default.conf - Run
certbot --nginxand provide details asked.
- Settings --> General --> URL Base --> Add base (/radarr)
- Add below proxy in nginx configuration
location /radarr {
proxy_pass http://radarr:7878;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
}
- Restart containers.
- Settings --> General --> URL Base --> Add base (/sonarr)
- Add below proxy in nginx configuration
location /sonarr {
proxy_pass http://sonarr:8989;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
}
- Settings --> General --> URL Base --> Add base (/prowlarr)
- Add below proxy in nginx configuration
This may need to change configurations in indexers and base in URL.
location /prowlarr {
proxy_pass http://prowlarr:9696; # Comment this line if VPN is enabled.
# proxy_pass http://vpn:9696; # Uncomment this line if VPN is enabled.
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
}
- Restart containers.
Note: If VPN is enabled, then Prowlarr is reachable on vpn's service name
location /qbt/ {
proxy_pass http://qbittorrent:5080/; # Comment this line if VPN is enabled.
# proxy_pass http://vpn:5080/; # Uncomment this line if VPN is enabled.
proxy_http_version 1.1;
proxy_set_header Host http://qbittorrent:5080; # Comment this line if VPN is enabled.
# proxy_set_header Host http://vpn:5080; # Uncomment this line if VPN is enabled.
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_cookie_path / "/; Secure";
}
Note: If VPN is enabled, then qbittorrent is reachable on vpn's service name
- Add base URL, Admin Dashboard -> Networking -> Base URL (/jellyfin)
- Add below config in Ngix config
location /jellyfin {
return 302 $scheme://$host/jellyfin/;
}
location /jellyfin/ {
proxy_pass http://jellyfin:8096/jellyfin/;
proxy_pass_request_headers on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
# Disable buffering when the nginx proxy gets very resource heavy upon streaming
proxy_buffering off;
}
Currently Jellyseerr/Overseerr doesnot officially support the subfolder/path reverse proxy. They have a workaround documented here without an official support. Find it here
location / {
proxy_pass http://127.0.0.1:5055;
proxy_set_header Referer $http_referer;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Real-Port $remote_port;
proxy_set_header X-Forwarded-Host $host:$remote_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Port $remote_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Ssl on;
}
- Restart containers
location /glance/ {
proxy_pass http://glance:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
}
location /portainer/ {
proxy_pass http://portainer:9000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /beszel/ {
proxy_pass http://beszel:8090/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
}
- Settings --> General --> URL Base --> Add base (/bazarr)
- Add below proxy in nginx configuration
location /bazarr {
proxy_pass http://bazarr:6767;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
}
- Restart containers
Neither the author nor the developers of the code in this repository condone or encourage downloading, sharing, seeding, or peering of copyrighted material.
Such activities are illegal under international laws.This project is intended for educational purposes only.
