359 lines
8.8 KiB
Markdown
359 lines
8.8 KiB
Markdown
# 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`:**
|
|
|
|
```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`:**
|
|
|
|
```yaml
|
|
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:**
|
|
```bash
|
|
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:**
|
|
|
|
```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
|
|
|
|
```bash
|
|
# 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)
|
|
|
|
```bash
|
|
cd /mnt/tank/stacks/dozzle
|
|
docker compose up -d
|
|
docker compose down
|
|
```
|
|
|
|
### Gitea (Traefik-routed)
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
# /mnt/tank/stacks/servarr/.env
|
|
WIREGUARD_PRIVATE_KEY=your-wireguard-key
|
|
```
|
|
|
|
Reference in docker-compose:
|
|
```yaml
|
|
environment:
|
|
- WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
|
|
```
|
|
|
|
---
|
|
|
|
## Security Best Practices
|
|
|
|
1. **Don't expose VPN-routed services directly to internet**
|
|
- Use Tailscale/Wireguard for remote access
|
|
- Or use SSH tunnels
|
|
|
|
2. **Enable authentication on all services**
|
|
- Each *arr app has built-in auth
|
|
- Configure strong passwords
|
|
|
|
3. **Use Traefik middlewares for additional security**
|
|
- Basic auth
|
|
- IP whitelisting
|
|
- Rate limiting
|
|
|
|
4. **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)
|
|
```
|