A complete, production-ready Docker Compose stack designed specifically for Proxmox LXC. It features a VPN-gated torrent client, internal HTTPS via self-signed wildcard certificates, and the full media automation suite using Hardlinks for instant file moves.
- 🔒 VPN Kill-Switch:
qBittorrentis routed throughGluetun. If the VPN tunnel drops, all traffic stops instantly. - 🏎️ Atomic Moves (Hardlinks): Media is not copied; it is hardlinked. A 50GB movie imports in 0.1 seconds and uses no extra storage.
- 🛡️ Internal TLS: Traefik v3 handles HTTPS for all services using a generated local Wildcard Certificate.
- 👻 LAN Only: No ports are exposed to the public internet. No Cloudflare Tunnels.
- 📱 Jellyseerr: Integrated request management optimized for Jellyfin.
graph TD
User((User Devices)) -->|HTTPS *.domain.com| Traefik
Traefik -->|Internal Routing| Sonarr
Traefik -->|Internal Routing| Radarr
Traefik -->|Internal Routing| Jellyseerr
Traefik -->|Internal Routing| Jellyfin
Traefik -->|Internal Routing| qBittorrent
subgraph VPN_Stack [VPN Namespace]
Gluetun[Gluetun VPN] -->|WireGuard| Internet
qBittorrent -->|Network: Service| Gluetun
end
subgraph Storage [Host Path /data]
Torrents[(/torrents)]
Media[(/media)]
end
qBittorrent -.->|Read/Write| Torrents
Sonarr -.->|Hardlink| Media
Radarr -.->|Hardlink| Media
Jellyfin -.->|Read| Media
This stack requires specific permissions on the Proxmox Host to allow VPN tunneling and Docker nesting.
- Stop the container.
- On the Proxmox Host Shell, edit
/etc/pve/lxc/YOUR-VM-ID.conf:
# Essential for Docker
features: nesting=1
# Essential for WireGuard/VPN (TUN Device)
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
# Optional: GPU Passthrough (Intel QuickSync)
# lxc.cgroup2.devices.allow: c 226:* rwm
# lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file
- Start the container.
You must route your internal domain to the LXC IP (e.g., 192.168.1.100).
Option A: Cloudflare (Split Horizon)
- Create an A Record:
media.yourdomain.compointing to192.168.1.100(DNS Only / Gray Cloud). - Create CNAME Records:
sonarr,radarr,traefik, etc., pointing tomedia.yourdomain.com.
Option B: Local DNS (PiHole/AdGuard)
- Add a DNS rewrite:
*.yourdomain.com->192.168.1.100.
Run these commands inside the LXC terminal:
# Create project structure
mkdir -p /opt/stack/traefik/certs
mkdir -p /opt/stack/traefik/config
mkdir -p /opt/stack/config
# Create data structure
mkdir -p /data/{torrents,media}/{tv,movies}
# Set permissions for PUID 1000 (Standard Docker User)
chown -R 1000:1000 /data
chmod -R 775 /data
Generate a self-signed wildcard certificate for Traefik.
cd /opt/stack/traefik/certs
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout yourdomain.key \
-out yourdomain.crt \
-subj "/CN=*.yourdomain.com"
chmod 600 yourdomain.key
Note: Replace *.yourdomain.com with your actual domain.
- Clone/Copy Files: Place
docker-compose.ymlandtraefik/configs in/opt/stack. - Configure Environment:
cp .env.example .env
nano .env
Fill in your ProtonVPN Private Key and IP. 3. Start Services:
docker compose up -d| Service | URL | Notes |
|---|---|---|
| Traefik | https://traefik.domain.com |
Dashboard |
| Jellyseerr | https://overseerr.domain.com |
Request Media |
| Jellyfin | https://jellyfin.domain.com |
Media Player |
| Sonarr | https://sonarr.domain.com |
TV Automation |
| Radarr | https://radarr.domain.com |
Movie Automation |
| qBittorrent | https://qbittorrent.domain.com |
VPN Download Client |
Because qBittorrent runs inside the Gluetun network namespace, it does not have its own IP. When connecting Sonarr/Radarr to qBittorrent:
- Host:
gluetun(Do NOT useqbittorrentorlocalhost) - Port:
8080 - No SSL
Ensure your real IP is not leaking.
- Check qBittorrent IP (Should be VPN):
docker exec -it qbittorrent curl ifconfig.me- Check Sonarr IP (Should be LAN/Home):
docker exec -it sonarr curl ifconfig.meIf you enabled GPU passthrough:
docker exec -it jellyfin /usr/lib/jellyfin-ffmpeg/vainfo/opt/stack/
├── .env # Secrets (Not in Git)
├── docker-compose.yml # Main stack definition
├── traefik/
│ ├── certs/ # Stores .crt and .key
│ └── config/
│ ├── traefik.yml # Static config
│ └── dynamic.yml # TLS configuration
└── config/ # App configs (sonarr, radarr db, etc.)