A complete Docker Compose setup for running a Jellyfin media server with automated media management using the *arr stack (Radarr, Sonarr, Lidarr, Bazarr, Prowlarr), all routed through a VPN for privacy.
- Jellyfin: Media server for streaming your content
- Jellyseerr: Request management for movies and TV shows
- Radarr: Movie collection management
- Sonarr: TV show collection management
- Lidarr: Music collection management
- Bazarr: Subtitle management
- Prowlarr: Indexer management
- qBittorrent: Torrent client
- Gluetun: VPN client (supports ProtonVPN, NordVPN, and many others)
- Caddy: Reverse proxy with automatic HTTPS
- Homepage: Dashboard for all your services
- Uptime Kuma: Service monitoring
- JellyStat: Jellyfin statistics and analytics
- Docker and Docker Compose installed
- A VPN provider that supports WireGuard (e.g., ProtonVPN)
- A domain name (for external access via Caddy)
- Basic understanding of Docker and networking
git clone <your-repo-url>
cd media-server-configcp .env.example .envEdit .env and set your values:
# Your media storage location
DATA_DIR=/mnt/data
# Your timezone (find yours at: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
TIMEZONE=America/New_York
# User/Group IDs (run 'id' command to find yours)
PGID=1000
PUID=1000
# WireGuard VPN credentials (get from your VPN provider)
WIREGUARD_PRIVATE_KEY=your_private_key
WIREGUARD_ADDRESSES=10.2.0.2/32
WIREGUARD_DNS=10.2.0.1Edit Caddyfile and replace yourdomain.com with your actual domain:
media.yourdomain.com {
reverse_proxy jellyfin:8096
}
Edit docker-compose.yaml and update the network settings in the gluetun service:
- NETWORK_LOCAL=192.168.1.0/24 # Your local network range
- FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24 # Your local network rangeTo find your local network range:
ip route | grep defaultIn docker-compose.yaml, change the default JellyStat database passwords:
- POSTGRES_PASSWORD=YOUR_SECURE_PASSWORD_HERE # Choose a secure password
- JWT_SECRET=GENERATE_SECURE_JWT_SECRET_HERE # Generate: openssl rand -base64 32docker-compose up -dOrganize your media directory as follows:
/mnt/data/
├── media/
│ ├── movies/
│ ├── tv/
│ └── music/
└── torrents/
├── movies/
├── tv/
└── music/
This structure allows the *arr services to automatically move completed downloads to the appropriate media folders.
Once running, access your services at:
- Jellyfin: http://localhost:8096
- Jellyseerr: http://localhost:5055
- qBittorrent: http://localhost:8080
- Radarr: http://localhost:7878 (via Gluetun)
- Sonarr: http://localhost:8989 (via Gluetun)
- Lidarr: http://localhost:8686 (via Gluetun)
- Bazarr: http://localhost:6767 (via Gluetun)
- Prowlarr: http://localhost:9696 (via Gluetun)
- Homepage: http://localhost:3001
- Uptime Kuma: http://localhost:3002
- JellyStat: http://localhost:3003
- Navigate to http://localhost:8096
- Follow the setup wizard
- Add your media libraries pointing to
/data/media/
- Access at http://localhost:8080
- Default credentials are typically
admin/adminadmin(verify in container logs if different:docker logs qbittorrent) - Important: Change the default password immediately in Settings → Web UI
- Set downloads folder to
/data/torrents/ - Configure categories for movies, tv, and music
- Add indexers for your content sources
- Configure API keys for Radarr, Sonarr, and Lidarr
- Add Prowlarr as indexer source
- Add qBittorrent as download client
- Set root folders to
/data/media/movies,/data/media/tv,/data/media/music
- Connect to your Jellyfin server
- Add Radarr and Sonarr for automatic requests
This setup uses Gluetun to route specific services through a VPN:
- qBittorrent (torrent traffic)
- Radarr, Sonarr, Lidarr (indexer searches)
- Bazarr, Prowlarr
Services that don't need VPN (Jellyfin, Jellyseerr, Homepage) connect directly.
Gluetun supports many VPN providers. See the full list.
For ProtonVPN WireGuard setup:
- Log into ProtonVPN
- Go to Downloads → WireGuard configuration
- Copy the Private Key, Address, and DNS values
- Update your
.envfile
- Change default passwords for JellyStat database and qBittorrent
- Do not commit your
.envfile (it's in.gitignore) - Use strong passwords for all services
- Keep your VPN credentials secure
- Regularly update Docker images:
docker-compose pull && docker-compose up -d
Check Gluetun logs:
docker logs gluetunEnsure NETWORK_LOCAL and FIREWALL_OUTBOUND_SUBNETS are set correctly in the Gluetun configuration.
Ensure PUID and PGID in .env match your user:
idIf ports are already in use, modify the port mappings in docker-compose.yaml:
ports:
- "8097:8096" # Change 8096 to 8097 on hostImportant directories to backup:
./config/- All service configurations- Your media directory (if not backed up elsewhere)
Update all services:
docker-compose pull
docker-compose up -dUpdate specific service:
docker-compose pull jellyfin
docker-compose up -d jellyfinThis configuration is provided as-is for personal use.
Feel free to submit issues and pull requests for improvements!