8.8 KiB
Integrating Servarr Stack with Traefik
Your Current Setup
You have a Servarr media automation stack (servarr.yaml) with:
- Gluetun - VPN container (ProtonVPN)
- qBittorrent - Torrent client
- Sonarr/Radarr/Lidarr - Media management
- Prowlarr - Indexer manager
- Bazarr - Subtitle downloader
- Recyclarr - Configuration sync
- Dozzle - Log viewer
- Watchtower - Auto-updater
Special consideration: Most services use network_mode: service:gluetun, meaning they share gluetun's network namespace for VPN routing.
Challenge: VPN Network Mode
Services using network_mode: service:gluetun cannot directly connect to the traefik_proxy network. They're locked into gluetun's network namespace for VPN routing.
Options for External Access
Option 1: Don't Expose VPN Services Externally (Recommended)
- Keep VPN-routed services local-only
- Access via local network or Tailscale/Wireguard
- Most secure approach
- Expose only Dozzle (log viewer) via Traefik for monitoring
Option 2: Expose Gluetun to Traefik
- Connect gluetun container to Traefik network
- Route to services through gluetun's exposed ports
- More complex configuration
- Potential security considerations
Option 3: Split the Stack
- Keep VPN services in current stack (local-only)
- Move non-VPN services (Dozzle) to separate stack for Traefik access
Recommended Approach: Hybrid Setup
Keep VPN Services Local, Expose Dozzle via Traefik
This gives you:
- ✅ Secure VPN routing for download/automation apps (local access only)
- ✅ External access to Dozzle for log monitoring
- ✅ Simple configuration
- ✅ No VPN security compromise
Implementation
Current Directory Structure
/mnt/tank/
├── configs/
│ ├── gluetun/
│ ├── qbittorrent/
│ ├── sonarr/
│ ├── radarr/
│ ├── lidarr/
│ ├── prowlarr/
│ ├── bazarr/
│ ├── recyclarr/
│ └── dozzle/
└── media/
Modified servarr.yaml
Keep the current file mostly as-is, but extract Dozzle to a separate stack:
Step 1: Deploy Traefik (if not done)
See: Traefik Multi-Stack Setup#Step 1
Step 2: Split Dozzle to Separate Stack
Remove Dozzle from servarr.yaml:
# Remove this section from servarr.yaml:
dozzle:
image: amir20/dozzle:latest
container_name: dozzle
ports:
- 9999:8080
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
restart: unless-stopped
Create /mnt/tank/stacks/dozzle/docker-compose.yml:
version: '3.8'
networks:
traefik_proxy:
external: true
services:
dozzle:
image: amir20/dozzle:latest
container_name: dozzle
restart: unless-stopped
networks:
- traefik_proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- TZ=Europe/Amsterdam
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik_proxy"
- "traefik.http.routers.dozzle.rule=Host(`logs.yourdomain.com`)"
- "traefik.http.routers.dozzle.entrypoints=websecure"
- "traefik.http.routers.dozzle.tls.certresolver=cloudflare"
- "traefik.http.services.dozzle.loadbalancer.server.port=8080"
# Optional: Add authentication
- "traefik.http.routers.dozzle.middlewares=dozzle-auth"
- "traefik.http.middlewares.dozzle-auth.basicauth.users=admin:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/"
Deploy Dozzle:
mkdir -p /mnt/tank/stacks/dozzle
cd /mnt/tank/stacks/dozzle
# Create docker-compose.yml with content above, then:
docker compose up -d
Now accessible at: https://logs.yourdomain.com
Step 3: Keep Servarr Stack As-Is
Your VPN-routed services remain accessible on local network:
- qBittorrent:
http://truenas-ip:8080 - Sonarr:
http://truenas-ip:8989 - Radarr:
http://truenas-ip:7878 - Prowlarr:
http://truenas-ip:9696 - Lidarr:
http://truenas-ip:8686 - Bazarr:
http://truenas-ip:6767
Access these via:
- Local network (current method)
- VPN (Tailscale/Wireguard) for remote access
- SSH tunnel for secure remote access
Alternative: Expose Select Services via Traefik
If you want some *arr services accessible externally (e.g., Sonarr/Radarr for mobile apps):
Option: Duplicate Ports in Gluetun
Add gluetun to Traefik network and route through it:
Modified servarr.yaml:
services:
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
cap_add:
- NET_ADMIN
environment:
- VPN_SERVICE_PROVIDER=protonvpn
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
- SERVER_COUNTRIES=Netherlands
- TZ=Europe/Amsterdam
volumes:
- /mnt/tank/configs/gluetun:/gluetun
- /mnt/tank/configs/gluetun-tmp:/tmp/gluetun
ports:
- 8080:8080 # qBittorrent
- 7878:7878 # Radarr
- 8686:8686 # Lidarr
- 8989:8989 # Sonarr
- 9696:9696 # Prowlarr
- 6767:6767 # Bazarr
networks:
- traefik_proxy # Add this
restart: unless-stopped
labels:
# Sonarr via Traefik
- "traefik.enable=true"
- "traefik.docker.network=traefik_proxy"
- "traefik.http.routers.sonarr.rule=Host(`sonarr.yourdomain.com`)"
- "traefik.http.routers.sonarr.entrypoints=websecure"
- "traefik.http.routers.sonarr.tls.certresolver=cloudflare"
- "traefik.http.routers.sonarr.service=sonarr"
- "traefik.http.services.sonarr.loadbalancer.server.port=8989"
# Radarr via Traefik
- "traefik.http.routers.radarr.rule=Host(`radarr.yourdomain.com`)"
- "traefik.http.routers.radarr.entrypoints=websecure"
- "traefik.http.routers.radarr.tls.certresolver=cloudflare"
- "traefik.http.routers.radarr.service=radarr"
- "traefik.http.services.radarr.loadbalancer.server.port=7878"
# Add more services as needed...
networks:
traefik_proxy:
external: true
# ... rest of services remain the same
Security Note: This exposes your download apps to the internet. Consider:
- Strong authentication in each app
- Traefik basic auth middleware
- Firewall rules limiting access to your IP
- Or stick with VPN-only access
Recommended Final Architecture
Internet (Port 80/443)
↓
Traefik
├── Gitea (https://git.yourdomain.com)
├── Dozzle (https://logs.yourdomain.com)
└── [Future Services]
Local Network Only:
↓
Servarr Stack (VPN-routed)
├── Sonarr (http://truenas-ip:8989)
├── Radarr (http://truenas-ip:7878)
├── qBittorrent (http://truenas-ip:8080)
└── [Other *arr apps]
Access Servarr remotely via:
- Tailscale/Wireguard VPN
- SSH tunnel:
ssh -L 8989:localhost:8989 user@truenas-ip
Stack Management
Servarr Stack
# Start/stop Servarr stack
cd /mnt/tank/stacks/servarr
docker compose up -d
docker compose down
# Update containers (Watchtower handles this automatically)
docker compose pull
docker compose up -d
Dozzle (Traefik-routed)
cd /mnt/tank/stacks/dozzle
docker compose up -d
docker compose down
Gitea (Traefik-routed)
cd /mnt/tank/stacks/gitea
docker compose up -d
docker compose down
All stacks are independent!
DNS Records for Cloudflare
Add A records:
| Name | Target | Proxy |
|---|---|---|
| git | Your-Public-IP | DNS only |
| logs | Your-Public-IP | DNS only |
| traefik | Your-Public-IP | DNS only |
Environment Variables
Create .env file in servarr directory for sensitive data:
# /mnt/tank/stacks/servarr/.env
WIREGUARD_PRIVATE_KEY=your-wireguard-key
Reference in docker-compose:
environment:
- WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
Security Best Practices
-
Don't expose VPN-routed services directly to internet
- Use Tailscale/Wireguard for remote access
- Or use SSH tunnels
-
Enable authentication on all services
- Each *arr app has built-in auth
- Configure strong passwords
-
Use Traefik middlewares for additional security
- Basic auth
- IP whitelisting
- Rate limiting
-
Keep Watchtower for auto-updates
- Already configured in your stack
- Keeps containers patched
Next Steps
- Deploy Traefik (if not already done)
- Move Dozzle to separate stack with Traefik labels
- Deploy Dozzle with external access
- Keep Servarr services local-only (current setup)
- Set up Tailscale/Wireguard for secure remote access to Servarr
- Add DNS records for exposed services
- Test accessing Dozzle externally
- Configure authentication for all services
Additional Notes
Your directory structure:
/mnt/tank/stacks/
├── traefik/
├── gitea/
├── servarr/ (your existing stack)
└── dozzle/ (optional split from servarr)