How I Built My Self-Hosted Media Stack
Self-hosting gives me full control over how I manage, consume, and organize digital media. Instead of relying on commercial streaming platforms, I run a fully automated media server on my own hardware — with each service containerized using Docker Compose.
Here’s an overview of the key apps in my setup and how they work together to automate everything from downloading to organizing and enriching my media library.
Homepage – My Homelab Dashboard
It all starts with a clean dashboard. I use Homepage as the front door to my self-hosted environment. It gives me quick access to all my services in a visually appealing layout, with real-time Docker stats and customizable widgets. It's minimal, fast, and easy to configure.
Radarr and Sonarr – Automated Media Management
Radarr and Sonarr form the core of my system, handling movies and TV shows respectively. They monitor upcoming releases, manage my watchlist, and automatically request downloads when new content becomes available. Once the content is downloaded, they organize everything into my media library with consistent naming and folder structure.
Prowlarr – Unified Indexer Management
Prowlarr connects everything to the outside world. It manages the indexers that Radarr and Sonarr use to search for content. Rather than configuring indexers in each app separately, Prowlarr centralizes that process, making everything easier to manage and maintain.
Bazarr – Subtitle Automation
To ensure everything I watch has accurate subtitles, I run Bazarr. It monitors my media library for missing or out-of-date subtitles and automatically fetches the best available versions. This is especially helpful for foreign content or when watching in noisy environments.
qBittorrent – Torrent Engine
All downloads go through qBittorrent, an open-source torrent client with a polished web interface. It works smoothly with Radarr, Sonarr, and Prowlarr to handle downloads efficiently. It’s lightweight, fast, and reliable.
FlareSolverr – Bypassing Web Protections
Some indexers use Cloudflare or other anti-bot protections, which can block automated tools like Prowlarr. FlareSolverr handles those challenges in the background, ensuring uninterrupted access to those sources.
MakeMKV – Ripping Physical Media
For converting DVDs and Blu-rays into digital files, I use MakeMKV. It provides a browser-based interface for ripping discs and outputs clean MKV files that can easily be added to my media library. It’s a great way to preserve old physical collections.
Unpackerr – Post-Processing and Cleanup
Once downloads complete, Unpackerr takes care of extracting and organizing any compressed files. It integrates with both Radarr and Sonarr to ensure media is in the right format and location, ready to be played without additional manual steps.
All Powered by Docker Compose
Every service runs as its own Docker container, I have put my compose and .env files below for your convenience.
Compose File:
#########################################################
networks:
mediastack:
name: mediastack
driver: bridge
ipam:
driver: default
services:
#########################################################
homepage:
image: ghcr.io/gethomepage/homepage:latest
container_name: homepage
restart: unless-stopped
ports:
- ${WEBUI_PORT_HOMEPAGE:?err}:3000
volumes:
- ${FOLDER_FOR_CONFIGS:?err}/homepage:/app/config
- /var/run/docker.sock:/var/run/docker.sock
environment:
- PUID=0
- PGID=0
# - UMASK=${UMASK:?err}
- TZ=${TIMEZONE:?err}
- HOMEPAGE_ALLOWED_HOSTS=home.fenici.com.au
networks:
- mediastack
#########################################################
bazarr:
image: lscr.io/linuxserver/bazarr:latest
container_name: bazarr
restart: unless-stopped
volumes:
- ${FOLDER_FOR_CONFIGS:?err}/bazarr:/config
- ${FOLDER_FOR_TORRENTS:?err}:/app/qBittorrent/downloads
- ${FOLDER_FOR_MOVIES:?err}:/movies
- ${FOLDER_FOR_TV_SHOWS:?err}:/tv/Tv_shows
environment:
- PUID=${PUID:?err}
- PGID=${PGID:?err}
- TZ=${TIMEZONE:?err}
- DOCKER_MODS=ghcr.io/themepark-dev/theme.park:bazarr
- TP_THEME=${TP_THEME:?err}
ports:
- ${WEBUI_PORT_BAZARR:?err}:6767
#########################################################
prowlarr:
image: lscr.io/linuxserver/prowlarr:develop
container_name: prowlarr
restart: unless-stopped
volumes:
- ${FOLDER_FOR_CONFIGS:?err}/prowlarr:/config
environment:
- PUID=${PUID:?err}
- PGID=${PGID:?err}
- TZ=${TIMEZONE:?err}
- DOCKER_MODS=ghcr.io/themepark-dev/theme.park:prowlarr
- TP_THEME=${TP_THEME:?err}
ports:
- ${WEBUI_PORT_PROWLARR:?err}:9696
#########################################################
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
restart: unless-stopped
volumes:
- ${FOLDER_FOR_CONFIGS:?err}/radarr:/config
- ${FOLDER_FOR_TORRENTS:?err}:/app/qBittorrent/downloads
- ${FOLDER_FOR_MOVIES:?err}:/movies/
environment:
- PUID=${PUID:?err}
- PGID=${PGID:?err}
- TZ=${TIMEZONE:?err}
- DOCKER_MODS=ghcr.io/themepark-dev/theme.park:radarr
- TP_THEME=${TP_THEME:?err}
ports:
- ${WEBUI_PORT_RADARR:?err}:7878
#########################################################
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
restart: unless-stopped
volumes:
- ${FOLDER_FOR_CONFIGS:?err}/sonarr:/config
- ${FOLDER_FOR_TORRENTS:?err}:/app/qBittorrent/downloads
- ${FOLDER_FOR_TV_SHOWS:?err}:/tv/Tv_shows
- /mnt/zenith/media/tv_shows:/tv/Tv_shows2
environment:
- PUID=${PUID:?err}
- PGID=${PGID:?err}
- TZ=${TIMEZONE:?err}
- DOCKER_MODS=ghcr.io/themepark-dev/theme.park:sonarr
- TP_THEME=${TP_THEME:?err}
ports:
- ${WEBUI_PORT_SONARR:?err}:8989
#########################################################
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
restart: unless-stopped
volumes:
- ${FOLDER_FOR_CONFIGS:?err}/qbittorrent:/config
- ${FOLDER_FOR_TORRENTS:?err}:/app/qBittorrent/downloads
- ${FOLDER_FOR_MEDIA:?err}:/app/qBittorrent/media
- ${FOLDER_FOR_MOVIES:?err}:/movies/
- ${FOLDER_FOR_TV_SHOWS:?err}:/tv/Tv_shows
environment:
- PUID=${PUID:?err}
- PGID=${PGID:?err}
# - UMASK=${UMASK:?err}
- TZ=${TIMEZONE:?err}
- WEBUI_PORT=${WEBUI_PORT_QBITTORRENT:?err}
- TORRENTING_PORT=${QBIT_PORT:?err}
- DOCKER_MODS=ghcr.io/themepark-dev/theme.park:qbittorrent
- TP_THEME=${TP_THEME:?err}
ports:
- ${WEBUI_PORT_QBITTORRENT:?err}:8200
- ${QBIT_PORT:?err}:6881
- ${QBIT_PORT:?err}:6881/udp
#########################################################
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
container_name: flaresolverr
restart: unless-stopped
environment:
- LOG_LEVEL=info
- LOG_HTML=false
- CAPTCHA_SOLVER=none
- TZ=${TIMEZONE:?err}
ports:
- ${FLARESOLVERR_PORT:?err}:8191
#########################################################
makemkv:
image: jlesage/makemkv
container_name: makemkv
restart: unless-stopped
ports:
- ${MAKEMKV_PORT:?err}:5800
volumes:
- ${FOLDER_FOR_CONFIGS:?err}/makemkv/appdata:/config:rw
- ${FOLDER_FOR_CONFIGS:?err}/makemkv/home/user:/storage:ro
- ${FOLDER_FOR_CONFIGS:?err}/makemkv/home/output:/output:rw
- ${FOLDER_FOR_MEDIA:?err}:/media
#########################################################
unpackerr:
image: golift/unpackerr
container_name: unpackerr
volumes:
- ${FOLDER_FOR_TORRENTS:?err}:/downloads
restart: always
user: ${PUID:?err}:${PGID:?err}
environment:
- TZ=${TIMEZONE:?err}
## Global Settings
- UN_DEBUG=false
- UN_QUIET=false
- UN_ERROR_STDERR=false
- UN_ACTIVITY=false
- UN_LOG_QUEUES=1m
- UN_LOG_FILE=/downloads/unpackerr.log
- UN_LOG_FILES=10
- UN_LOG_FILE_MB=10
- UN_INTERVAL=2m
- UN_START_DELAY=1m
- UN_RETRY_DELAY=5m
- UN_MAX_RETRIES=3
- UN_PARALLEL=1
- UN_FILE_MODE=0644
- UN_DIR_MODE=0755
## Web Server
- UN_WEBSERVER_LISTEN_ADDR=0.0.0.0:5656
## Folder Settings
- UN_FOLDERS_INTERVAL=1s
- UN_FOLDERS_BUFFER=20000
## Sonarr Settings
- UN_SONARR_0_URL=http://192.168.50.221:8989
- UN_SONARR_0_API_KEY=${SONARR_API_KEY:?err}
- UN_SONARR_0_PATHS_0=/downloads
- UN_SONARR_0_PROTOCOLS=torrent
- UN_SONARR_0_TIMEOUT=10s
- UN_SONARR_0_DELETE_DELAY=5m
- UN_SONARR_0_DELETE_ORIG=false
- UN_SONARR_0_SYNCTHING=false
## Radarr Settings
- UN_RADARR_0_URL=http://192.168.50.221:7878
- UN_RADARR_0_API_KEY=${RADARR_API_KEY:?err}
- UN_RADARR_0_PATHS_0=/downloads
- UN_RADARR_0_PROTOCOLS=torrent
- UN_RADARR_0_TIMEOUT=10s
- UN_RADARR_0_DELETE_DELAY=5m
- UN_RADARR_0_DELETE_ORIG=false
- UN_RADARR_0_SYNCTHING=false
Env File:
COMPOSE_PROJECT_NAME=mediastack
TP_THEME=overseerr
FOLDER_FOR_CONFIGS=/mnt/zenith/configs
FOLDER_FOR_MEDIA=/mnt/tank/media
FOLDER_FOR_MOVIES=/mnt/tank/media/Movies
FOLDER_FOR_TV_SHOWS=/mnt/tank/media/Tv_Shows
FOLDER_FOR_TORRENTS=/mnt/tank/torrents
PUID=568
PGID=568
TIMEZONE=Australia/Perth
# Backend Ports
QBIT_PORT=6881
FLARESOLVERR_PORT=8191
MAKEMKV_PORT=5800
# WebUI Ports
WEBUI_PORT_HOMEPAGE=3123
WEBUI_PORT_BAZARR=6767
WEBUI_PORT_PROWLARR=9696
WEBUI_PORT_RADARR=7878
WEBUI_PORT_SONARR=8989
WEBUI_PORT_QBITTORRENT=8200
# unpackerr options
SONARR_URL=
RADARR_URL=
SONARR_API_KEY=
RADARR_API_KEY=