Complete step-by-step setup and configuration guide for the Home Media Server stack.
- Docker Engine 20.10+ and Docker Compose 2.0+
- Linux system (Ubuntu 20.04+ recommended)
- Minimum 4GB RAM, 20GB storage (more depending on media collection)
- Tailscale account (free tier acceptable) for remote access
- Internet connection for initial setup
git clone <your-repository-url> /home/lab/home_media
cd /home/lab/home_mediaCopy the example environment file and fill in your values:
cp .env.example .env
nano .envRequired values:
TSDPROXY_AUTHKEY: Get from https://login.tailscale.com/admin/settings/keysTSDPROXY_HOSTNAME: Your Tailscale server IP (or runtailscale ip -4on your server)PUID/PGID: Runidto get your user/group IDsTZ: Your timezone (see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
Create the expected directory structure (linked to your existing data):
# Create media organization directories
mkdir -p /data/media/{movies,tv,music}
mkdir -p /data/torrents/{movies,tv,music}
# Set appropriate permissions (adjust user:group to match your PUID:PGID)
sudo chown -R 1000:1000 /data
sudo chmod -R 755 /dataNote: If migrating from existing setup, symlink existing folders:
# Example: Link existing movie data
ln -s /path/to/existing/movies /data/media/movies
# Create empty folders for other types
mkdir -p /data/media/tv /data/media/musiccd ent/
mkdir -p appdata/{radarr,sonarr,lidarr,bazarr,prowlarr,qbittorrent,jellyfin,termix,dockhand}
# Set permissions so container user (UID 1000) can write
sudo chown -R 1000:1000 appdata/
chmod -R 775 appdata/# Start all services
docker-compose up -d
# Verify all services started
docker-compose psAll services should show Up status. Check logs for any issues:
docker-compose logs -f # All services
docker-compose logs -f radarr # Specific serviceIf using TSDProxy for remote access (recommended):
# Check TSDProxy logs
docker-compose logs tsdproxy
# Verify Tailscale is connected
docker exec tsdproxy tailscale statusIf you see authentication prompts, it means the auth key needs to be finalized:
# Check for auth URL in logs
docker-compose logs tsdproxy | grep auth
# Open the URL in a browser to approve the deviceOnce approved, your services will be accessible at:
radarr.YOUR_TAILSCALE_HOSTNAMEsonarr.YOUR_TAILSCALE_HOSTNAME- etc.
Each service stores configuration in ./appdata/{service}/config.xml. After initial startup, configure each:
- Access at
http://localhost:7878(or via Tailscale) - Settings > Media Management:
- Movie Folder:
/data/media/movies - Naming:
- Standard:
{Movie Title} ({Release Year})/{Movie Title} ({Release Year}) - {Quality Full} - Root Folders: Add
/data/media
- Settings > Download Clients:
- Add qBittorrent client
- Host:
qbittorrent - Port:
8080
- Settings > Indexers:
- Leave empty or add via Prowlarr integration
- Access at
http://localhost:8989(or via Tailscale) - Settings > Media Management:
- TV Folder:
/data/media/tv - Naming:
- Series:
{Series Title} - Season:
Season {season:00} - Episode:
{Series Title} - {season:00}e{episode:00} - {Episode Title} {Quality Full} - Root Folders: Add
/data/media
- Settings > Download Clients: Add qBittorrent
- Settings > Indexers: Connect to Prowlarr
- Access at
http://localhost:8686(or via Tailscale) - Settings > Media Management:
- Music Folder:
/data/media/music - Naming:
- Artist:
{Artist Name} - Album:
{Album Title} ({Release Year}) - Track:
{track:00} - {Track Title} - Root Folders: Add
/data/media
- Settings > Download Clients: Add qBittorrent
- Settings > Metadata Provider: Configure preferred sources
- Access at
http://localhost:9696(or via Tailscale) - Settings > Apps:
- Add Radarr, Sonarr, Lidarr instances
- Get API keys from each service (Settings > General)
- Indexers: Add your preferred torrent/usenet indexers
- Access at
http://localhost:6767(or via Tailscale) - Settings > Sonarr / Settings > Radarr:
- Enable integration
- Add API keys from respective services
- Settings > Languages: Select subtitle languages to download
- Access at
http://localhost:8080(or via Tailscale) - Default credentials:
admin/adminadmin
- Highly recommend changing password immediately
- Options > Downloads:
- Default Save Path:
/data/torrents/ - Keep torrents: Enable (for seeding)
- Create download categories:
movies→/data/torrents/moviestv→/data/torrents/tvmusic→/data/torrents/music
- Access at
http://localhost:8096(or via Tailscale) - Libraries → Add Library:
- Movie Library:
/data/media/movies - TV Library:
/data/media/tv - Music Library:
/data/media/music
- Dashboard → Plugins: Install preferred plugins
- Playback: Configure subtitle, audio codec preferences
- Access at
http://localhost:8081(or via Tailscale) - First access generates JWT token and password - save these
- Use for system administration via browser
- Access at
http://localhost:3000(or via Tailscale) - Database auto-initialized in
./appdata/dockhand/ - Monitor and manage all Docker containers
The stack uses Trash Guides format for consistent organization:
/data/media/movies/
Inception (2010)/
Inception (2010) - 1080p.mkv
Inception (2010) - 1080p.srt
The Dark Knight (2008)/
Interstellar (2014)/
Radarr Naming:
{Movie Title} ({Release Year})/{Movie Title} ({Release Year}) - {Quality Full}
/data/media/tv/
Breaking Bad/
Season 01/
Breaking Bad - 01e01 - Pilot [1080p].mkv
Breaking Bad - 01e02 - Cat's in the Bag [1080p].mkv
...
Season 02/
Better Call Saul/
Sonarr Naming:
{Series Title}/Season {season:00}/{Series Title} - {season:00}e{episode:00} - {Episode Title} {Quality Full}
/data/media/music/
Taylor Swift/
Midnights (2022)/
01 - Midnight.flac
02 - Karma.flac
...
The Beatles/
Abbey Road (1969)/
The White Album (1968)/
Dua Lipa/
Lidarr Naming:
{Artist Name}/{Album Title} ({Release Year})/{track:00} - {Track Title}
# All services
docker-compose logs -f
# Specific service (follow in real-time)
docker-compose logs -f radarr
# Last 50 lines
docker-compose logs --tail=50 sonarr
# Specific time range
docker-compose logs --since 2024-01-15 --until 2024-01-16 lidarr# Pull latest images
docker-compose pull
# Restart with new images
docker-compose up -d
# Verify all services healthy
docker-compose ps# Backup all application data
tar -czf home_media_backup_$(date +%Y%m%d_%H%M%S).tar.gz \
ent/appdata/ \
.env
# Store securely (never commit .env!)# Extract backup
tar -xzf home_media_backup_YYYYMMDD_HHMMSS.tar.gz
# Restart stack
docker-compose down
docker-compose up -d
# Services should retain all settings# Stop stack without removing volumes
docker-compose stop
# Restart later
docker-compose start
# Full cleanup (removes containers, keeps volumes)
docker-compose down
# Full cleanup INCLUDING volumes (caution!)
docker-compose down -v- Check logs:
docker-compose logs {service-name}- Common issues:
- Port already in use: Change ports in
.env - Permission denied: Run
sudo chown -R 1000:1000 appdata/ - Disk space: Check with
df -h /data
- Verify qBittorrent is accessible
- Ensure download categories exist in qBittorrent
- Check Radarr/Sonarr can connect to qBittorrent:
- Settings > Download Clients > Test Connection
- Verify
/data/torrentshas write permissions:
touch /data/torrents/test.txt
rm /data/torrents/test.txt- Verify library paths point to correct directories
- Check permissions:
ls -la /data/media/movies - Force library scan: Jellyfin Dashboard > Scan All Libraries
- Check Jellyfin logs for encoding errors
- Verify Tailscale is installed on host
- Check TSDProxy container logs:
docker-compose logs tsdproxy- Verify auth key is valid (check Tailscale admin console)
- Ensure Docker socket is mounted:
docker-compose ps tsdproxy
- Get API keys:
- Radarr: Settings > General > API Key
- Sonarr: Settings > General > API Key
- Lidarr: Settings > General > API Key
- Test connection:
curl -X GET "http://localhost:7878/api/v3/system/status?apikey=YOUR_KEY"- For Prowlarr integration, ensure correct hostname:
- Use container name, not
localhost:http://radarr:7878
To add additional services or integrate with other stacks:
- Edit
docker-compose.yml - Add new service with labels:
new-service:
image: your-image
networks:
- default
labels:
tsdproxy.enable: "true"
tsdproxy.name: "your-service-name"- Access at
your-service-name.YOUR_TAILSCALE_HOSTNAME
For large media libraries, consider:
- Dedicated disk: Mount
/datato separate SSD/HDD - Read-only mounts: Jellyfin uses
:rotag (read-only) - Docker volumes: Named volumes (
tsdproxydata:) persist outside containers
To save disk space and remove unused images/containers:
# Remove stopped containers
docker container prune
# Remove unused images
docker image prune
# Remove unused volumes
docker volume prune
# Full cleanup (all unused)
docker system prune -aReduce resource usage by:
- Disabling TSDProxy if not using Tailscale
- Limiting Jellyfin transcoding: Settings > Playback > Transcoding
- Running media analysis during off-peak hours
- Reducing Docker logging: Change
json-filetononein compose
Optimize for many concurrent users/downloads:
- Allocate more resources:
docker update --cpus 2 container_name - Use dedicated hardware: Separate server for downloads
- Enable qBittorrent rate limiting
- Use Jellyfin direct streaming (avoid transcoding)
- Check service logs (see Operations section)
- Verify all prerequisites are met
- Test network connectivity:
docker-compose exec radarr ping sonarr - Search GitHub issues in relevant projects:
- Radarr: github.com/radarr/radarr
- Sonarr: github.com/sonarr/sonarr
- Jellyfin: github.com/jellyfin/jellyfin
For detailed service documentation:
- Radarr: https://wiki.servarr.com/en/radarr
- Sonarr: https://wiki.servarr.com/en/sonarr
- Lidarr: https://wiki.servarr.com/en/lidarr
- Jellyfin: https://jellyfin.org/docs/
- qBittorrent: https://www.qbittorrent.org/
- Prowlarr: https://wiki.servarr.com/en/prowlarr
- Bazarr: https://www.bazarr.media/