A complete home media server setup with automated downloads, streaming, and AI-powered control
- ๐ Kavita (Ebooks/Comics)
- โ๏ธ Nextcloud
- ๐พ Duplicati (Backups)
- ๐ณ Portainer
- ๐ Watchtower
- ๐พ TrueNAS Integration
This stack provides a complete automated media server with:
- ๐ Reverse Proxy: Traefik with automatic HTTPS (Let's Encrypt)
- ๐ฅ Media Automation: Radarr, Sonarr, Prowlarr for automated downloads
- ๐ฅ Secure Downloads: qBittorrent behind NordVPN
- ๐ฌ Streaming: Plex with hardware transcoding
- ๐ Subtitles: Bazarr for 40+ languages
- ๐ Books & Comics: Kavita reader
- ๐ฏ User Requests: Seerr for family/friends
- ๐ Dashboard: Homarr for monitoring
- ๐ Auto-Updates: Watchtower
- ๐พ Backups: Duplicati with encryption
User Request (Seerr)
โ
Quality Check (Radarr/Sonarr)
โ
Search Indexers (Prowlarr)
โ
Download via VPN (qBittorrent + NordVPN)
โ
Organize & Rename (Radarr/Sonarr)
โ
Add Subtitles (Bazarr)
โ
Stream (Plex)
- ๐ง OS: Linux (tested on Debian 11/12)
- ๐ณ Docker: Engine 20.10+
- ๐ฆ Docker Compose: V2
- ๐พ Storage:
- 50GB+ for system & configs
- As much as possible for media (500GB - multiple TB)
- ๐ฅ๏ธ RAM: 8GB minimum, 16GB+ recommended
- โก CPU: Multi-core recommended for transcoding
- ๐ฎ GPU (Optional): NVIDIA for hardware transcoding
- ๐ Domain Name: Pointed to your server IP
- ๐ Let's Encrypt: DNS accessible on ports 80/443
- ๐ VPN: NordVPN account (or modify for other providers)
- ๐บ Plex Account: Free or Plex Pass
- โ๏ธ Cloudflare (Optional): For DNS management
- Basic Linux command line
- Docker & Docker Compose basics
- Basic networking (ports, DNS)
- (Optional) Understanding of BitTorrent/Usenet
# Clone the repository
git clone https://github.com/YOUR_USERNAME/htpc-box-docker.git
cd htpc-box-docker
# Create environment file
cp .env.example .env
nano .env # or use your favorite editorEdit .env with your specific configuration:
##############################################
# User & Group (IMPORTANT!)
##############################################
# Get your UID/GID by running: id
PUID=1000
PGID=1000
##############################################
# Timezone
##############################################
TZ=Europe/Paris
##############################################
# Storage Paths
##############################################
# Main storage root (your large drive)
ROOT=/mnt/media
# Configuration storage (can be smaller, needs backups)
CONFIG=/config
##############################################
# Domain Configuration
##############################################
# Your domain name (without protocol)
SERVERNAME=yourdomain.com
##############################################
# VPN Configuration
##############################################
VPN_USER=your_email@example.com
VPN_PWD=your_nordvpn_password
VPN_COUNTRY=US # or CH, UK, etc.
NORDVPN_PROTOCOL=nordlynx # fastest option
##############################################
# Database Configuration
##############################################
MYSQL_PASSWORD=generate_secure_password_here
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
##############################################
# Optional: Plex Claim Token
##############################################
# Get from: https://www.plex.tv/claim/
# PLEX_CLAIM=claim-xxxxxxxxxxxx# Create all required directories
mkdir -p ${ROOT}/{downloads,movies,tv,books,music}
mkdir -p ${ROOT}/downloads/{incomplete,complete}
mkdir -p ${ROOT}/downloads/complete/{movies,tv,music}
mkdir -p ${CONFIG}
# Set permissions
sudo chown -R ${PUID}:${PGID} ${ROOT} ${CONFIG}
chmod -R 755 ${ROOT} ${CONFIG}# Start Traefik first (reverse proxy)
docker compose up -d traefik
# Wait 30 seconds for Traefik to initialize
sleep 30
# Start all remaining services
docker compose up -d
# Check status
docker compose ps
# View logs
docker compose logs -f๐ Done! Your services are now starting up. Access them at:
- Dashboard:
https://homarr.${SERVERNAME} - Traefik:
https://traefik.${SERVERNAME}
โ ๏ธ First-time setup: Most services will require initial configuration. See the Service Integration Guide below.
This setup assumes a dedicated storage location for media and configuration:
/mnt/media/ # Your large storage (NAS mount or local drive)
โโโ downloads/
โ โโโ incomplete/ # Active downloads
โ โโโ complete/
โ โโโ movies/ # Completed movie downloads
โ โโโ tv/ # Completed TV downloads
โ โโโ music/ # Completed music downloads
โโโ movies/ # Organized movie library (Plex source)
โโโ tv/ # Organized TV library (Plex source)
โโโ books/ # Ebook and comic library
โโโ music/ # Music library
/config/ # Service configurations (needs backup!)
โโโ radarr/
โโโ sonarr/
โโโ plex-server/
โโโ ...
If you're using a NAS (TrueNAS, Synology, etc.) for media storage, you'll want to mount it automatically.
sudo apt update
sudo apt install cifs-utils -ysudo mkdir -p /mnt/nas-share
sudo chown ${PUID}:${PGID} /mnt/nas-shareEdit /etc/fstab:
sudo nano /etc/fstabAdd this line at the end (replace with your NAS details):
# NAS Media Storage
//192.168.1.100/media /mnt/nas-share cifs username=your_nas_user,password=your_nas_password,uid=1000,gid=1000,rw,iocharset=utf8,file_mode=0777,dir_mode=0777,_netdev,noauto,vers=3.1.1,cache=strict,actimeo=86400,x-systemd.automount 0 0Parameter Breakdown:
| Parameter | Purpose |
|---|---|
//192.168.1.100/media |
NAS IP and share name |
/mnt/nas-share |
Local mount point |
cifs |
SMB/CIFS protocol |
username=... |
NAS login username |
password=... |
NAS login password |
uid=1000,gid=1000 |
Mount as your user (from .env PUID/PGID) |
rw |
Read-write access |
iocharset=utf8 |
UTF-8 support (for international characters) |
file_mode=0777,dir_mode=0777 |
Full permissions for all files/dirs |
_netdev |
Wait for network before mounting |
noauto |
Don't mount at boot (use with automount) |
vers=3.1.1 |
SMB protocol version (3.1.1 is modern and secure) |
cache=strict |
Strict caching (safer for multiple clients) |
actimeo=86400 |
Cache directory listings for 24h (performance) |
x-systemd.automount |
Auto-mount when accessed (not at boot) |
# Test mount manually
sudo mount -a
# Verify it worked
df -h | grep nas-share
# Check access
ls -la /mnt/nas-share
# Test write access
touch /mnt/nas-share/test.txt
rm /mnt/nas-share/test.txtPoint your storage paths to the NAS mount:
# In .env
ROOT=/mnt/nas-share
CONFIG=/config # Keep configs on local storage (faster, for backups)/etc/fstab is a security risk.
โ Better Solution: Use a credentials file
-
Create credentials file:
sudo nano /root/.nas-credentials
-
Add credentials:
username=your_nas_user password=your_nas_password
-
Secure the file:
sudo chmod 600 /root/.nas-credentials sudo chown root:root /root/.nas-credentials
-
Update fstab entry:
//192.168.1.100/media /mnt/nas-share cifs credentials=/root/.nas-credentials,uid=1000,gid=1000,rw,iocharset=utf8,file_mode=0777,dir_mode=0777,_netdev,noauto,vers=3.1.1,cache=strict,actimeo=86400,x-systemd.automount 0 0
Mount fails:
# Check mount errors
sudo mount -v /mnt/nas-share
# Check NAS connectivity
ping 192.168.1.100
# Test SMB connection manually
smbclient //192.168.1.100/media -U your_nas_userPerformance issues:
- Try different
vers=(3.0, 2.1, 3.1.1) - Adjust
cache=(strict, loose, none) - Check network speed:
iperf3 -c 192.168.1.100
Permission denied:
- Verify
uid/gidmatch your user - Check NAS share permissions
- Ensure user has access on NAS side
Automate maintenance tasks with cron jobs for backups and updates.
Edit the system crontab:
sudo nano /etc/crontabAdd these entries at the end:
# Cron format:
# m h dom mon dow user command
# | | | | | | |
# | | | | | | +-- Command to execute
# | | | | | +-------- Day of week (0-7, Sun=0 or 7)
# | | | | +------------ Month (1-12)
# | | | +---------------- Day of month (1-31)
# | | +-------------------- Hour (0-23)
# | +---------------------- Minute (0-59)
##############################################
# Daily Docker Update & Cleanup (6:00 AM)
##############################################
0 6 * * * root bash -c 'cd /home/htpc/htpc-box-docker && docker compose down && docker compose pull --ignore-pull-failures && docker compose up -d --remove-orphans && docker image prune -a -f'What it does:
- Backs up
/config/directory to NAS - Preserves all service configurations
- Runs daily at 5:30 AM (before updates)
Command breakdown:
rsync -aHAX /config/ /mnt/nas-share/backup-htpc-config/| Flag | Purpose |
|---|---|
-a |
Archive mode (preserve permissions, timestamps, etc.) |
-H |
Preserve hard links |
-A |
Preserve ACLs |
-X |
Preserve extended attributes |
Restore from backup:
# If you need to restore configs
sudo rsync -aHAX /mnt/nas-share/backup-htpc-config/ /config/
sudo chown -R ${PUID}:${PGID} /config/What it does:
- Stops all containers gracefully
- Pulls latest images (ignores failures)
- Starts containers with new images
- Removes orphaned containers
- Cleans up old images to save space
Command breakdown:
cd /home/htpc/htpc-box-docker
docker compose down # Stop all services
docker compose pull --ignore-pull-failures # Pull new images
docker compose up -d --remove-orphans # Start with new images
docker image prune -a -f # Remove old imagesRuns: Daily at 6:00 AM (low-traffic time)
Want different times? Use crontab.guru to generate schedules:
Examples:
# Every 6 hours
0 */6 * * * root command
# Every Sunday at 3 AM
0 3 * * 0 root command
# Twice daily (6 AM and 6 PM)
0 6,18 * * * root command
# Every weekday at noon
0 12 * * 1-5 root commandView cron logs:
# Live cron execution log
sudo tail -f /var/log/syslog | grep CRON
# View recent cron jobs
sudo grep CRON /var/log/syslog | tail -20Test cron job manually:
# Run backup manually
sudo rsync -aHAX /config/ /mnt/nas-share/backup-htpc-config/
# Run update manually
cd /home/htpc/htpc-box-docker
sudo docker compose down
sudo docker compose pull
sudo docker compose up -dGet notified when cron jobs complete:
Install mail utilities:
sudo apt install mailutils postfix -yConfigure postfix (choose "Internet Site", set hostname)
Update cron to send email:
# Add at top of /etc/crontab
MAILTO=your-email@example.com
# Cron will email output of any command that produces outputIf you prefer manual updates (more control):
Option 1: Disable in crontab:
# Comment out the line in /etc/crontab
# 0 6 * * * root bash -c 'cd /home/htpc/htpc-box-docker ...'Option 2: Disable Watchtower:
# In docker-compose.yml, comment out or remove:
# watchtower:
# ...Then update manually:
cd ~/htpc-box-docker
docker compose pull
docker compose up -dgraph TD
subgraph "Public Internet"
User((User))
end
subgraph "HTPC Stack (Docker)"
Traefik[๐ Traefik Proxy]
Auth[๐ PocketID / Auth]
Dashboard[๐ Homarr Dashboard]
subgraph "Media Discovery & Requests"
Seerr[๐ฌ Seerr Requests]
end
subgraph "Media Pipeline"
Prowlarr[๐ Prowlarr Indexers]
Radarr[๐ฅ Radarr Movies]
Sonarr[๐บ Sonarr TV]
Bazarr[๐ Bazarr Subtitles]
Tdarr[๐๏ธ Tdarr Transcoding]
end
subgraph "Download Client"
NordVPN[๐ NordVPN]
qBittorrent[๐ฅ qBittorrent]
FlareSolverr[๐ง FlareSolverr]
end
subgraph "Media Hosting"
Plex[๐ฌ Plex Server]
Kavita[๐ Kavita Reader]
end
subgraph "Utilities"
Portainer[๐ณ Portainer]
Nextcloud[โ๏ธ Nextcloud]
Duplicati[๐พ Duplicati]
end
end
User --> Traefik
Traefik --> Dashboard
Traefik --> Seerr
Traefik --> Auth
Seerr --> Radarr
Seerr --> Sonarr
Radarr --> Prowlarr
Sonarr --> Prowlarr
Radarr --> qBittorrent
Sonarr --> qBittorrent
qBittorrent -.-> NordVPN
Prowlarr -.-> FlareSolverr
Bazarr --> Radarr
Bazarr --> Sonarr
Radarr --> Plex
Sonarr --> Plex
Plex -.-> Tdarr
| Service | Purpose | Port | URL |
|---|---|---|---|
| ๐ Traefik | Reverse Proxy | 80, 443 | traefik.${SERVERNAME} |
| ๐ Forward Auth | SSO Authentication | - | auth.${SERVERNAME} |
| ๐ Homarr | Dashboard | - | homarr.${SERVERNAME} |
| ๐ฌ Seerr | Media Requests | - | seerr.${SERVERNAME} |
| ๐ฅ Radarr | Movie Management | - | radarr.${SERVERNAME} |
| ๐บ Sonarr | TV Management | - | sonarr.${SERVERNAME} |
| ๐ Prowlarr | Indexer Manager | - | prowlarr.${SERVERNAME} |
| ๐ Bazarr | Subtitles | - | bazarr.${SERVERNAME} |
| ๐ NordVPN | VPN Tunnel | - | (internal) |
| ๐ฅ qBittorrent | Download Client | - | qbittorrent.${SERVERNAME} |
| ๐ฌ Plex | Media Server | 32400 | http://server-ip:32400/web |
| ๐๏ธ Tdarr | Transcoding | - | tdarr.${SERVERNAME} |
| ๐ Kavita | Ebook/Comic Reader | - | kavita.${SERVERNAME} |
| ๏ฟฝ๏ฟฝ Swaparr | Stalled Manager | - | Background |
| โ๏ธ Nextcloud | File Sync | - | nextcloud.${SERVERNAME} |
| ๐พ Duplicati | Backups | - | duplicati.${SERVERNAME} |
| ๐ณ Portainer | Container Manager | - | portainer.${SERVERNAME} |
The Traefik dashboard provides real-time visibility into your routing and service health.
Container: traefik
Ports: 80 (HTTP), 443 (HTTPS)
Web UI: https://traefik.${SERVERNAME}
Traefik automatically routes traffic to services and handles HTTPS certificates via Let's Encrypt.
- Certificates: Stored in
./letsencrypt/acme.json - Dashboard: Protected by forward-auth
- Automatic service discovery via Docker labels
- โ Automatic HTTPS certificates
- โ HTTP to HTTPS redirect
- โ Service discovery
- โ Load balancing
- โ Security headers
Container: traefik-forward-auth
Purpose: Centralized SSO authentication
Provides OAuth2 authentication for all Traefik-protected services.
-
Choose OAuth Provider (Google, GitHub, etc.)
-
Create OAuth App:
- Google: Console
- GitHub: Settings โ Developer โ OAuth Apps
-
Set Callback URL:
https://auth.${SERVERNAME}/_oauth -
Configure Environment (in docker-compose.yml):
environment:
- PROVIDERS_GOOGLE_CLIENT_ID=your_client_id
- PROVIDERS_GOOGLE_CLIENT_SECRET=your_secret
- SECRET=generate_random_secret_here
- AUTH_HOST=auth.${SERVERNAME}
- COOKIE_DOMAIN=${SERVERNAME}- Restart Service:
docker compose up -d traefik-forward-auth
Self-hosted identity provider and authentication gateway for your stack.
Container: pocketid
Web UI: https://auth.${SERVERNAME}
Self-hosted identity provider for complete control over authentication.
- Access web UI
- Create admin account
- Configure services to use PocketID
- Add users/groups
A sleek, customizable dashboard to quickly access and monitor all your services.
Container: homarr
Web UI: https://homarr.${SERVERNAME}
Beautiful, customizable dashboard for all your services with integrations and monitoring.
- Access Dashboard: Navigate to URL
- Add Services:
- Click "+" to add service tiles
- Configure icons, URLs, and descriptions
- Add Widgets:
- Weather
- Calendar
- Media requests
- System stats
- Configure Integrations:
- Radarr/Sonarr API keys
- Plex token
- Download client stats
- ๐ System resources
- ๐ฌ Recent media additions
- ๐ฅ Download queue
- ๐ก๏ธ Weather
- ๐ Calendar
Beautiful request and discovery platform for Plex.
Container: seerr
Web UI: https://seerr.${SERVERNAME}
Beautiful request and discovery platform for Plex. Perfect for letting family/friends request content.
- Access Seerr web UI
- Click "Sign in with Plex"
- Authorize Seerr
- Select your Plex server
- Enable libraries to sync (Movies, TV Shows)
- Start initial sync (may take a while)
- Settings โ Services โ Radarr
- Add server:
- Server Name: Radarr
- Hostname/IP:
radarr(container name) - Port:
7878 - API Key: Get from Radarr โ Settings โ General โ Security
- Quality Profile: Select default (HD-1080p recommended)
- Root Folder:
/movies
- Test and Save
- Settings โ Services โ Sonarr
- Add server:
- Server Name: Sonarr
- Hostname/IP:
sonarr - Port:
8989 - API Key: From Sonarr settings
- Quality Profile: Select default
- Root Folder:
/tv
- Test and Save
- Settings โ Users
- Import Plex users
- Set permissions:
- Admin: Full access
- User: Request and view status
- Quotas: Optional limits per user
- Discord
- Telegram
- Slack
- Webhook
- Users can browse and request content
- Automatic approval or admin review
- Status tracking (pending โ downloading โ available)
- Notifications when content is ready
Indexer manager for integrating Torrent and Usenet search.
Container: prowlarr
Web UI: https://prowlarr.${SERVERNAME}
Centralized indexer management. Add indexers once, sync to all *arr apps automatically.
- Indexers โ Add Indexer
- Search for your trackers/indexers
- Common public indexers:
- RARBG (if available)
- 1337x
- The Pirate Bay
- YTS
- For private trackers:
- Enter credentials
- Configure rate limits
- Settings โ Indexers
- Enable FlareSolverr
- URL:
http://flaresolverr:8191
- Settings โ Apps โ Add Application
- Select Radarr
- Configure:
- Prowlarr Server:
http://prowlarr:9696 - Radarr Server:
http://radarr:7878 - API Key: From Radarr settings
- Prowlarr Server:
- Test and Save
- Add Application โ Sonarr
- Configure:
- Prowlarr Server:
http://prowlarr:9696 - Sonarr Server:
http://sonarr:8989 - API Key: From Sonarr settings
- Prowlarr Server:
- Test and Save
- Settings โ Apps
- Click "Full Sync" for each app
- Verify indexers appear in Radarr/Sonarr
- โ Add indexers once, use everywhere
- โ Centralized management
- โ Automatic category mapping
- โ Built-in health checks
Automated movie download and collection management.
Container: radarr
Web UI: https://radarr.${SERVERNAME}
Automated movie downloading, renaming, and organization.
- Settings โ Download Clients โ Add โ qBittorrent
- Configure:
- Name: qBittorrent
- Host:
qbittorrent(container name) - Port:
8112 - Password:
qbittorrent(change in qBittorrent first!) - Category:
radarr-movies
- Test and Save
- Settings โ Media Management โ Root Folders
- Add root folder:
/movies
- Settings โ Profiles
- Review/edit quality profiles:
- Any: Grabs first available
- HD-1080p: Recommended for most
- Ultra-HD: For 4K content (large files!)
- Settings โ Media Management
- Enable "Rename Movies"
- Recommended format:
{Movie Title} ({Release Year}) [imdbid-{ImdbId}]
- Movies โ Add New
- Search for a movie
- Select quality profile
- Monitor: Yes
- Add Movie
- Movie added โ searches indexers
- Finds match โ sends to qBittorrent
- Download completes โ moves to
/movies - Renames and organizes automatically
- Updates Plex library
Automated TV series download and collection management.
Container: sonarr
Web UI: https://sonarr.${SERVERNAME}
Automated TV show downloading with episode tracking and season management.
- Settings โ Download Clients โ Add โ qBittorrent
- Configure:
- Name: qBittorrent
- Host:
qbittorrent - Port:
8112 - Password: (qBittorrent password)
- Category:
sonarr-tv
- Test and Save
- Settings โ Media Management โ Root Folders
- Add root folder:
/tv
- Settings โ Media Management
- Enable "Rename Episodes"
- Format:
- Standard:
{Series Title} - S{season:00}E{episode:00} - {Episode Title} - Daily:
{Series Title} - {Air-Date} - {Episode Title} - Anime:
{Series Title} - {absolute:000} - {Episode Title}
- Standard:
- Settings โ Profiles
- Common profiles:
- HD-720p/1080p: Good balance
- Any: Fastest availability
- Series โ Add New
- Search for a show
- Configure:
- Monitor: All episodes / Future episodes / First season
- Quality Profile: Select appropriate
- Season Folder: Yes
- Add Series
- All: Downloads all episodes (including old)
- Future: Only new episodes from now on
- Missing: Searches for missing episodes
- First Season: Test before committing to full series
Companion application to download missing subtitles.
Container: bazarr
Web UI: https://bazarr.${SERVERNAME}
Automatic subtitle downloading for your media in 40+ languages.
- Settings โ Radarr
- Enable and configure:
- Address:
http://radarr:7878 - API Key: From Radarr settings
- Address:
- Test and Save
- Settings โ Sonarr
- Enable and configure:
- Address:
http://sonarr:8989 - API Key: From Sonarr settings
- Address:
- Test and Save
- Settings โ Providers
- Recommended providers:
- OpenSubtitles: Free account required
- Subscene: No account needed
- Addic7ed: Good for TV shows
- For each provider:
- Enable
- Add credentials if required
- Set language priority
- Settings โ Languages
- Languages Filter:
- Add languages you want (e.g., English, French)
- Default Settings:
- Single Language: If you only want one language
- Default Enabled: For new movies/shows
- Settings โ Scheduler
- Configure:
- Search Subtitles: Every 6 hours recommended
- Upgrade Subtitles: Weekly
- โ Automatic subtitle search
- โ Multi-language support
- โ Hearing impaired subtitles
- โ Manual search option
- โ Subtitle upgrade (better quality)
Container: nordvpn
Purpose: VPN tunnel for secure downloading
Routes qBittorrent traffic through NordVPN to protect privacy.
Set in .env:
VPN_USER=your_email@example.com
VPN_PWD=your_password
VPN_COUNTRY=US
NORDVPN_PROTOCOL=nordlynx # fastestUS- United StatesUK- United KingdomCH- SwitzerlandNL- Netherlands- Check NordVPN docs for full list
# Check if VPN is connected
docker compose logs nordvpn
# Test external IP (should be VPN IP, not your real IP)
docker exec nordvpn curl https://ipinfo.io
BitTorrent client routing all traffic through NordVPN.
Container: qbittorrent
Web UI: https://qbittorrent.${SERVERNAME}
Default Password: qbittorrent
BitTorrent client running behind NordVPN for secure downloads.
- Login with password:
qbittorrent - Preferences โ Interface โ Password
- Set a strong password
- Preferences โ Downloads
- Set:
- Download to:
/downloads/incomplete - Move completed to:
/downloads/complete - Auto-managed: Yes
- Download to:
- Preferences โ Plugins
- Enable "Label" plugin
- This allows Radarr/Sonarr to categorize downloads
- Right-click in main window โ Label โ Add
- Create labels:
radarr-moviessonarr-tv
- For each label โ Options:
- Move Completed To:
/downloads/complete/movies(for radarr)/downloads/complete/tv(for sonarr)
- Move Completed To:
Add a Linux ISO torrent to verify:
- Download starts
- VPN is working (check IP)
- Moves to complete folder
Container: flaresolverr
Port: 8191 (internal)
Proxy server that bypasses Cloudflare protection for indexers.
Automatically used by Prowlarr when enabled. No manual configuration needed.
Your personal Netflix - stream media to any device, anywhere.
Container: plex-server
Web UI: http://<your-server-ip>:32400/web
Your personal Netflix - stream media to any device, anywhere.
- Access web UI
- Sign in with Plex account
- Follow setup wizard
- Settings โ Libraries โ Add Library
- Type: Movies
- Add folder:
/media/movies - Scanner: Plex Movie
- Agent: Plex Movie
- Add Library โ TV Shows
- Add folder:
/media/tv - Scanner: Plex TV Series
- Agent: Plex Series
If you have an NVIDIA GPU:
- Settings โ Transcoder
- Transcoder quality: Automatic
- Enable: Use hardware acceleration when available
- Hardware transcoding device: Select your GPU
- Settings โ Remote Access
- Enable "Manually specify public port"
- Configure port forwarding on your router:
32400
Requires:
- NVIDIA GPU in host
- NVIDIA drivers installed
nvidia-docker-runtimeconfigured
Check GPU access:
docker exec plex-server nvidia-smi- Hardware transcoding (multiple streams)
- Mobile sync
- Live TV & DVR
- Skip intro detection
- 4K streaming
Containers: scheduler, plextraktsync
Syncs your Plex watch history with Trakt.tv for tracking across platforms.
-
Create Trakt API App:
- Go to Trakt API Apps
- Create application
- Note Client ID and Secret
-
Configure Authentication:
docker exec -it plextraktsync bash
plextraktsync
# Follow authentication prompts- Schedule Sync:
- Configured via
schedulercontainer - Default: Every 6 hours
- Configured via
Distributed audio and video transcoding and library management.
Container: tdarr
Web UI: https://tdarr.${SERVERNAME}
Distributed transcoding system for optimizing your media library.
- Reduce file sizes (H.264 โ H.265)
- Convert audio formats
- Remove unwanted subtitle tracks
- Standardize video quality
- Save storage space
-
Add Library:
- Add
/media/moviesand/or/media/tv
- Add
-
Choose Plugins:
- Transcode to H.265 (HEVC)
- Audio normalization
- Subtitle extraction
-
Configure Workers:
- CPU workers for software transcoding
- GPU workers for hardware acceleration (NVIDIA)
Transcoding is CPU/GPU intensive and can take days for large libraries. Start with a test folder!
Digital library for ebooks, comics, manga, and PDFs with a beautiful web reader.
Container: kavita
Web UI: https://kavita.${SERVERNAME}
Digital library for ebooks, comics, manga, and PDFs with a beautiful web reader.
- Create Account: First user is admin
- Add Library:
- Settings โ Libraries โ Add
- Type: Comics / Books / Manga
- Path:
/books
- Scan Library: Trigger initial scan
- Configure OPDS (for reader apps):
- Enable OPDS feed
- Use with apps like Chunky (iOS), Tachiyomi (Android)
- Comics: CBZ, CBR, CB7, CBT
- Ebooks: EPUB, PDF
- Images: ZIP, RAR with images
Self-hosted file sync and collaboration - your own Dropbox/Google Drive.
Container: nextcloud
Web UI: https://nextcloud.${SERVERNAME}
Self-hosted file sync and collaboration - your own Dropbox/Google Drive.
-
Complete Setup Wizard:
- Create admin account
- Connect to MariaDB database:
- User:
nextcloud(from .env) - Password:
${MYSQL_PASSWORD} - Database:
nextcloud - Host:
database:5432
- User:
-
Install Recommended Apps:
- Calendar
- Contacts
- Notes
- Deck (kanban)
- Talk (chat/video)
-
Configure Cron:
- Administration โ Basic settings
- Background jobs โ Cron
- Already configured in docker-compose
-
Desktop/Mobile Sync:
- Download Nextcloud client for your devices
- Connect using your URL and credentials
Securely stores encrypted, incremental, compressed remote backups.
Container: duplicati
Web UI: https://duplicati.${SERVERNAME}
Encrypted backup solution supporting cloud storage providers.
Essential (small, critical):
/config- All service configurations- Docker compose files
.envfile (store securely!)
Optional (large):
- Plex metadata
- Download history
- Watch status
Don't Backup:
- Media files (too large, easily re-acquired)
- Incomplete downloads
-
Add Backup Job:
- Home โ Add backup
-
Choose Destination:
- S3/B2/Google Drive/FTP/etc.
- Configure credentials
-
Select Source:
/config
-
Configure Schedule:
- Daily at 3 AM recommended
-
Set Encryption:
- AES-256
- Save passphrase securely!
Powerful container management platform.
Container: portainer
Web UI: https://portainer.${SERVERNAME}
Visual Docker management interface for monitoring and controlling containers.
- ๐ Container stats and logs
- ๐๏ธ Start/stop/restart containers
- ๐ Edit container configs
- ๐ฅ๏ธ Console access
- ๐ Resource monitoring
Container: watchtower
Automatically updates Docker containers when new images are available.
Only updates containers with label:
labels:
- com.centurylinklabs.watchtower.enable=trueDefault schedule: Daily at 4 AM
Container: truenas
Custom container for TrueNAS API integration (if you use TrueNAS for storage).
Follow this order for seamless integration:
NordVPN โ qBittorrent
- Verify VPN is connected
- Setup qBittorrent labels for movies/tv
- Change default password
- Test with a legal torrent
FlareSolverr โ Prowlarr
- Add FlareSolverr to Prowlarr
- Add 3-5 indexers (public or private)
- Test each indexer
Prowlarr โ Radarr โ qBittorrent
Prowlarr โ Sonarr โ qBittorrent
Radarr:
- Add qBittorrent as download client
- Add
/moviesroot folder - Connect Prowlarr app
- Sync indexers
- Add a test movie
Sonarr:
- Add qBittorrent as download client
- Add
/tvroot folder - Connect Prowlarr app
- Sync indexers
- Add a test TV show
Radarr/Sonarr โ Bazarr โ OpenSubtitles
- Connect Bazarr to Radarr
- Connect Bazarr to Sonarr
- Add subtitle providers
- Configure languages
- Enable automatic search
Radarr/Sonarr โ Plex
- Claim Plex server
- Add movie library (
/media/movies) - Add TV library (
/media/tv) - Configure transcoding
- Setup remote access
Plex โ Seerr โ Radarr/Sonarr
- Sign in to Seerr with Plex
- Sync Plex libraries
- Connect Radarr (with API key)
- Connect Sonarr (with API key)
- Configure user permissions
All Services โ Homarr
- Add service tiles
- Configure widgets
- Add API integrations
- Request: Use Seerr to request a movie
- Watch: It should:
- โ Appear in Radarr
- โ Search indexers via Prowlarr
- โ Send to qBittorrent (through VPN)
- โ Download complete
- โ
Move to
/moviesfolder - โ Get renamed by Radarr
- โ Fetch subtitles via Bazarr
- โ Appear in Plex
- โ Mark as "Available" in Seerr
๐ If all steps work: Your media pipeline is fully automated!
This stack uses a custom bridge network npm_proxy with static IPs:
networks:
npm_proxy:
name: npm_proxy
driver: bridge
ipam:
config:
- subnet: 192.168.89.0/24Services are assigned static IPs (e.g., 192.168.89.100-120) for predictable networking.
Services are exposed via Traefik using labels:
labels:
- "traefik.enable=true"
- "traefik.http.routers.SERVICE.rule=Host(`SERVICE.${SERVERNAME}`)"
- "traefik.http.routers.SERVICE.entrypoints=websecure"
- "traefik.http.routers.SERVICE.tls=true"
- "traefik.http.routers.SERVICE.tls.certresolver=letsencrypt"
- "traefik.http.services.SERVICE.loadbalancer.server.port=PORT"Protect services with different auth levels:
# Admin only
- "traefik.http.routers.SERVICE.middlewares=auth-admin"
# Any authenticated user
- "traefik.http.routers.SERVICE.middlewares=auth-user"
# No auth (service handles it)
- "traefik.http.routers.SERVICE.middlewares=sanitize-headers"# Start all services
docker compose up -d
# Start specific service
docker compose up -d SERVICE_NAME
# Rebuild and start (use after code/image changes)
docker compose up -d --build SERVICE_NAME
# Stop all services
docker compose down
# Stop but keep data
docker compose stop
# Restart service
docker compose restart SERVICE_NAME
# View logs (live)
docker compose logs -f SERVICE_NAME
# View last 100 lines
docker compose logs --tail 100 SERVICE_NAME
# View status
docker compose ps
# Validate config (catches errors)
docker compose config
# Remove stopped containers
docker compose rm# List all containers
docker ps -a
# Stop all running containers
docker stop $(docker ps -q)
# Remove all stopped containers
docker rm $(docker ps -a -q)
# Remove all unused images
docker image prune -a
# Remove all unused volumes (โ ๏ธ deletes data!)
docker volume prune
# Remove all unused networks
docker network prune
# Complete cleanup (โ ๏ธ dangerous!)
docker system prune -a --volumes# Live logs with timestamps
docker logs --tail 50 --follow --timestamps CONTAINER_NAME
# Get shell in container
docker exec -it CONTAINER_NAME bash
# Run single command
docker exec CONTAINER_NAME ls -la /config
# Check resource usage
docker stats
# Inspect container details
docker inspect CONTAINER_NAME
# View container processes
docker top CONTAINER_NAME# Docker disk usage breakdown
docker system df -v
# Analyze directory sizes (interactive)
ncdu /mnt/media
# Quick directory size
du -sh /mnt/media/*
# Check disk space
df -h# Test VPN connection
docker exec nordvpn curl https://ipinfo.io
# Should show VPN IP, not your real IP
# Check container network
docker network inspect npm_proxy
# Test Traefik routing
curl -H "Host: radarr.yourdomain.com" http://localhost
# DNS resolution inside container
docker exec radarr nslookup google.com
# Check open ports
netstat -tulpn | grep LISTEN# Radarr: Trigger search for all missing movies
# (via API - get API key from Radarr settings)
curl -X POST "http://localhost:7878/api/v3/command" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"missingMoviesSearch"}'
# Sonarr: Similar search
curl -X POST "http://localhost:8989/api/v3/command" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"missingEpisodeSearch"}'
# Plex: Scan library
docker exec plex-server \
'/usr/lib/plexmediaserver/Plex Media Scanner' --scan --refresh \
--section 1 # Section ID from library settings
# qBittorrent: List active torrents
docker exec qbittorrent qbittorrent-console "info"Symptoms:
- HTTPS not working
- Browser shows "insecure connection"
- Services accessible via IP but not domain
Diagnosis:
# Check Traefik logs
docker compose logs traefik | grep -i error
# Verify DNS
nslookup yourdomain.com
# Check ports are open
nc -zv yourdomain.com 80
nc -zv yourdomain.com 443Solutions:
-
Verify DNS:
- Domain must point to your server IP
- Wait for DNS propagation (up to 48h)
-
Check Firewall:
sudo ufw allow 80/tcp sudo ufw allow 443/tcp
-
Force Certificate Renewal:
# Stop Traefik docker compose stop traefik # Delete certificates rm -rf ./letsencrypt/acme.json # Restart docker compose up -d traefik # Watch logs docker compose logs -f traefik
Symptoms:
- 404 or 502 error
- "Service Unavailable"
Diagnosis:
# Check service is running
docker compose ps SERVICE_NAME
# Check logs
docker compose logs SERVICE_NAME
# Inspect Traefik routes
docker compose exec traefik cat /etc/traefik/traefik.yml
# Check network
docker network inspect npm_proxySolutions:
-
Verify Labels:
- Check docker-compose.yml
- Ensure service has correct Traefik labels
- Port must match service's internal port
-
Check Network:
# Ensure service is on npm_proxy network docker inspect SERVICE_NAME | grep -A 10 Networks
-
Restart Service:
docker compose restart SERVICE_NAME traefik
Symptoms:
- qBittorrent can't download torrents
- "No incoming connections" warning
- Downloads stuck at 0%
Diagnosis:
# Check VPN status
docker compose logs nordvpn
# Test VPN IP
docker exec nordvpn curl https://ipinfo.io
# Check qBittorrent can reach internet
docker exec qbittorrent ping -c 3 8.8.8.8Solutions:
-
Verify VPN Credentials:
- Check
.envfile - Ensure
VPN_USERandVPN_PWDare correct
- Check
-
Restart VPN:
docker compose restart nordvpn # Wait 30 seconds docker compose restart qbittorrent -
Change VPN Server:
- Edit
.env - Try different country:
VPN_COUNTRY=CH - Restart services
- Edit
-
Check VPN Protocol:
- Try
nordlynx(fastest) oropenvpn_tcp(most compatible)
- Try
Symptoms:
- "Permission denied" errors
- Services can't write files
- Libraries not updating
Diagnosis:
# Check file ownership
ls -la ${ROOT}
ls -la ${CONFIG}
# Check PUID/PGID in .env
echo "PUID: $PUID"
echo "PGID: $PGID"
# Get your current user IDs
idSolutions:
-
Fix Ownership:
sudo chown -R ${PUID}:${PGID} ${ROOT} sudo chown -R ${PUID}:${PGID} ${CONFIG}
-
Fix Permissions:
sudo chmod -R 755 ${ROOT} sudo chmod -R 755 ${CONFIG}
-
Verify PUID/PGID:
- Make sure they match your user
- Run
idto confirm - Update
.envif needed - Restart services
Symptoms:
- Nextcloud can't connect to database
- "Connection refused" errors
Diagnosis:
# Check database is running
docker compose ps database
# Check logs
docker compose logs database
# Test connection
docker exec database psql -U nextcloud -c "SELECT 1;"Solutions:
-
Restart Database:
docker compose restart database
-
Reset Database (
โ ๏ธ deletes data):docker compose down database docker volume rm htpc-box-docker_database-data docker compose up -d database
-
Verify Credentials:
- Check
.envfile - Ensure passwords match
- Check
Symptoms:
- Disk full warnings
- Services can't write files
Diagnosis:
# Check Docker usage
docker system df -v
# Check media directories
du -sh ${ROOT}/*
# Find large files
find ${ROOT} -type f -size +10GSolutions:
-
Clean Docker:
# Remove unused images docker image prune -a # Remove stopped containers docker container prune # Remove unused volumes (โ ๏ธ careful!) docker volume prune
-
Clean Downloads:
# Remove completed downloads (if already imported) rm -rf ${ROOT}/downloads/complete/* # Remove incomplete/failed downloads rm -rf ${ROOT}/downloads/incomplete/*
-
Clean Plex Cache:
# Find transcode cache du -sh ${CONFIG}/plex-server/Library/Application\ Support/Plex\ Media\ Server/Cache/Transcode # Remove (safe - Plex recreates) rm -rf ${CONFIG}/plex-server/Library/Application\ Support/Plex\ Media\ Server/Cache/Transcode/*
-
Use Tdarr:
- Transcode H.264 โ H.265 (50% smaller)
- Remove unnecessary audio tracks
- Compress oversized files
Symptoms:
- Container keeps restarting
- Service exits immediately
Diagnosis:
# Check logs
docker compose logs SERVICE_NAME
# Check for port conflicts
sudo netstat -tulpn | grep PORT_NUMBER
# Inspect container
docker inspect SERVICE_NAMESolutions:
-
Check Logs:
- Often shows exact error
- Look for "ERROR" or "FATAL"
-
Port Conflict:
- Another service using the port
- Change port in docker-compose.yml
-
Volume Permissions:
- Check ownership of mounted volumes
-
Corrupted Config:
# Backup and reset mv ${CONFIG}/SERVICE_NAME ${CONFIG}/SERVICE_NAME.backup docker compose up -d SERVICE_NAME
Symptoms:
- "No results" when searching
- Indexers show as "Down"
- Timeout errors
Diagnosis:
# Check Prowlarr
docker compose logs prowlarr
# Check FlareSolverr (for Cloudflare-protected indexers)
docker compose logs flaresolverr
# Test indexer URL
docker exec radarr curl -I https://indexer-url.comSolutions:
-
Verify Prowlarr Sync:
- Prowlarr โ Settings โ Apps
- Click "Full Sync"
- Check indexers appear in Radarr/Sonarr
-
Enable FlareSolverr:
- Prowlarr โ Settings โ Indexers
- FlareSolverr URL:
http://flaresolverr:8191
-
Check Indexer Status:
- Some indexers go down
- Add backup indexers
Symptoms:
- New media doesn't appear in Plex
- Library scan doesn't find files
Diagnosis:
# Check Plex logs
docker compose logs plex-server | grep -i scan
# Verify files exist
ls -la ${ROOT}/movies
ls -la ${ROOT}/tv
# Check permissions
ls -la ${ROOT}/movies | headSolutions:
-
Manual Scan:
- Library โ โฎ Menu โ Scan Library Files
-
Fix Permissions:
chown -R ${PUID}:${PGID} ${ROOT}/movies ${ROOT}/tv
-
Verify Library Path:
- Settings โ Libraries โ Edit
- Folder should be
/media/movies(not/movies)
-
Restart Plex:
docker compose restart plex-server
-
๐ Change Default Passwords:
- qBittorrent:
qbittorrentโ strong password - Portainer: Set during first login
- All services: Use unique, strong passwords
- qBittorrent:
-
๐ Use Strong Credentials:
- Minimum 16 characters
- Use password manager (Bitwarden, 1Password)
- Never reuse passwords
-
๐ซ Never Commit Secrets:
# Add to .gitignore .env letsencrypt/ config/ -
๐ Regular Updates:
# Manual update all images docker compose pull docker compose up -d # Or enable Watchtower (automatic)
-
๐ฅ Firewall Configuration:
# Only expose necessary ports sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow 22/tcp # SSH sudo ufw allow 80/tcp # HTTP (redirects to HTTPS) sudo ufw allow 443/tcp # HTTPS sudo ufw allow 32400/tcp # Plex (if using remote access) sudo ufw enable
-
๐ VPN for Downloads:
- Keep qBittorrent behind NordVPN
- Never expose download client to internet
-
๐พ Regular Backups:
# Use Duplicati to backup: ${CONFIG}/ # All service configs .env # Environment variables (encrypted!) docker-compose.yml # Service definitions
-
๐ Enable 2FA:
- Nextcloud: Settings โ Security โ Two-Factor
- Portainer: Settings โ Authentication
- Forward Auth: OAuth provider 2FA
-
๐ต๏ธ Monitor Logs:
# Check for suspicious activity docker compose logs | grep -i "failed\|error\|unauthorized"
-
๐ Keep Host Secure:
# Regular system updates sudo apt update && sudo apt upgrade -y # Install fail2ban (blocks brute force) sudo apt install fail2ban -y
Don't:
- โ Expose Radarr/Sonarr/qBittorrent directly to internet
- โ Use default passwords
- โ Run containers as root (use PUID/PGID)
- โ Commit
.envto public repos - โ Disable SSL/HTTPS
- โ Use weak passwords
Do:
- โ Use reverse proxy (Traefik)
- โ Enable HTTPS everywhere
- โ Keep services updated
- โ Use VPN for downloads
- โ Regular backups
- โ Monitor access logs
We welcome contributions! Here's how you can help:
- Use GitHub Issues
- Include logs and error messages
- Describe steps to reproduce
- New service integrations
- Configuration optimizations
- Documentation improvements
- Fork the repository
- Create feature branch
- Make changes
- Test thoroughly
- Submit PR with description
- Fix typos
- Add examples
- Clarify instructions
- Translate to other languages
This stack is built using excellent Docker images from:
- LinuxServer.io: Radarr, Sonarr, Prowlarr, Bazarr, qBittorrent, Kavita, Duplicati
- Traefik Labs: Traefik reverse proxy
- Plex Inc: Plex Media Server
- Seerr: Request management
- Homarr: Dashboard
- Nextcloud: File sync
- Tdarr: Media transcoding
- Community: Countless contributors and maintainers
- r/selfhosted community
- Docker community
- Open source maintainers everywhere
This docker-compose configuration is provided as-is under the MIT License.
Individual services have their own licenses:
- Plex: Proprietary (free & paid tiers)
- Radarr/Sonarr: GPL-3.0
- Traefik: MIT
- See each project for specific licensing