Skip to content
 
 

Repository files navigation

"Buy Me A Coffee"

media-stack

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

Requirements

  • 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.

Install media stack

⚠️ 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:

  1. With a VPN (Recommended)
  2. Without a VPN
  3. 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-vpn profile.
This requirement prevents accidental or unintentional deployment of media-stack without VPN.

Running the docker compose command 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 range

When 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

Configure Your VPN Provider

Refer to your VPN provider's documentation to generate an OpenVPN username and password.
For setup instructions, check:
➡️ Gluetun VPN Setup Guide

Enabling VPN in docker-compose.yml

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.

Deploying the Stack with VPN (NordVPN Example)

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 -d

Static Container IP Requirement

A 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_IP
  • SONARR_STATIC_CONTAINER_IP

Deploy the Stack Without VPN

🚨 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 -d

Deploy the Stack with Recommendarr (Optional)

Recommendarr 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)

Deploying with Recommendarr

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

Configure qBittorrent

  • Open qBitTorrent at http://localhost:5080. Default username is admin. Temporary password can be collected from container log docker 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

Configure Radarr

  • 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 vpn in 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.

Configure Bazarr

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.)

Configure Jellyfin

  • 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

Configure FlareSolverr

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

Service Access URLs

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

Configure Jellyseerr

  • 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/

Configure Prowlarr

  • 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.

Configure Recommendarr

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 admin and password 1234
  • 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

Configure Glance

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

Configure Beszel

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

Configure Portainer

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

Configure Nginx

  • 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.

Apply SSL in Nginx

  • 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 --nginx and provide details asked.

Radarr Nginx reverse proxy

  • 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.

Sonarr Nginx reverse proxy

  • 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;
  }

Prowlarr Nginx reverse proxy

  • 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

qBittorrent Nginx proxy

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

Jellyfin Nginx proxy

  • 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;
    }

Jellyseerr Nginx proxy

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

Glance Nginx reverse proxy

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;
}

Portainer Nginx reverse proxy

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";
}

Beszel Nginx reverse proxy

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;
}

Bazarr Nginx reverse proxy

  • 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

Disclaimer

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.

About

A self-hosted stack for media management and streaming, with AI-powered movie and show recommendations. Includes Sonarr, Radarr, qBitTorrent, Prowlarr, Jellyfin, Jellyseerr, Recommendarr, and VPN support.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors