If you run qBittorrent, Prowlarr, Sonarr and friends in Docker, the standard advice is to give each one its own VPN connection. That works, but it’s wasteful and hard to manage. Gluetun is a lightweight VPN client container that acts as a network gateway — every container that needs VPN protection just borrows its network stack instead of running its own tunnel.
One tunnel. One set of credentials. One place to check if something isn’t connecting.
WireGuard endpoint
- Interface: wg0
- Kill-switch: iptables — ON
- Ports exposed: 8080, 6881, 9696…
Port forwarding: VPN provider assigns an external port → Gluetun → qBittorrent
What you need before you start
- A VPN subscription that supports WireGuard (Mullvad, PIA, ProtonVPN, AirVPN, and many others)
- Your provider’s WireGuard private key — generated from your provider’s dashboard
- The VPN endpoint address and port (e.g.
198.54.128.1:51820) - Your assigned internal VPN IP (e.g.
10.66.44.2/32) - The server’s public key
All of these come from your provider’s “Generate WireGuard config” page. Download a .conf file — everything you need is in it.
Mullvad tip: Go to Account → WireGuard configuration → Generate key → Download config. Pick a server that supports port forwarding if you want qBittorrent to accept incoming connections.
The Gluetun Docker Compose file
Create /mnt/tank/stacks/gluetun/docker-compose.yml:
services:
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
ports:
# qBittorrent web UI
- 8080:8080
# qBittorrent torrent port
- 6881:6881
- 6881:6881/udp
# Prowlarr
- 9696:9696
# Add ports for any other containers sharing this network
environment:
- VPN_SERVICE_PROVIDER=mullvad # change to your provider
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=YOUR_PRIVATE_KEY_HERE
- WIREGUARD_ADDRESSES=10.66.44.2/32 # your assigned VPN IP
- SERVER_COUNTRIES=Netherlands # or Sweden, Switzerland, etc.
# Optional: lock to a specific city or hostname
# - SERVER_CITIES=Amsterdam
# - WIREGUARD_PUBLIC_KEY=SERVER_PUBLIC_KEY
# - VPN_ENDPOINT_IP=198.54.128.1
# - VPN_ENDPOINT_PORT=51820
# Port forwarding (Mullvad / PIA)
- VPN_PORT_FORWARDING=on
- VPN_PORT_FORWARDING_PROVIDER=mullvad
volumes:
- /mnt/tank/configs/gluetun:/gluetun
restart: unless-stopped
healthcheck:
test: ["CMD", "/gluetun-entrypoint", "healthcheck"]
interval: 30s
timeout: 10s
retries: 3
Environment variables explained
| Variable | What it does |
|---|---|
VPN_SERVICE_PROVIDER |
Your VPN provider slug (mullvad, pia, protonvpn, airvpn, etc.) |
VPN_TYPE |
Use wireguard — faster and simpler than OpenVPN |
WIREGUARD_PRIVATE_KEY |
Your WireGuard private key from the provider dashboard |
WIREGUARD_ADDRESSES |
The internal IP assigned to you by the VPN (from the .conf file) |
SERVER_COUNTRIES |
Which country to connect to. Gluetun picks the best server automatically |
VPN_PORT_FORWARDING |
Enable port forwarding (needed for good torrent speeds on some providers) |
Security note: Never commit
WIREGUARD_PRIVATE_KEYto git. Store it in a.envfile alongside the compose file and reference it as${WIREGUARD_PRIVATE_KEY}.
Connecting other containers
This is the key part. Any container that should route through the VPN uses network_mode: "container:gluetun" instead of declaring its own ports. It piggybacks entirely on Gluetun’s network interface — same IP, same routing table, same kill-switch.
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: "container:gluetun" # <-- this is all you need
environment:
- PUID=568
- PGID=568
- WEBUI_PORT=8080
volumes:
- /mnt/tank/configs/qbittorrent:/config
- /mnt/tank/downloads:/downloads
restart: unless-stopped
depends_on:
gluetun:
condition: service_healthy
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
container_name: prowlarr
network_mode: "container:gluetun"
environment:
- PUID=568
- PGID=568
volumes:
- /mnt/tank/configs/prowlarr:/config
restart: unless-stopped
depends_on:
gluetun:
condition: service_healthy
A few things to note:
- No
ports:on the child containers — all port mappings go on the Gluetun service, since that’s the one with the actual network interface. depends_on: condition: service_healthy— this makes the container wait until Gluetun has a working VPN connection before starting. Essential for the kill-switch to work on startup.- Containers can reach each other via
localhost— because they share the same network namespace, qBittorrent and Prowlarr can talk to each other on127.0.0.1.
The kill-switch
Gluetun uses iptables to block all traffic that doesn’t go through the VPN tunnel. If the WireGuard connection drops, the containers sharing its network simply lose internet access rather than falling back to your real IP. No leaks.
The CAP_NET_ADMIN capability and /dev/net/tun device are required for this — Gluetun needs to create the network interface and set the firewall rules.
Port forwarding
Some VPN providers (Mullvad, PIA, AirVPN) can assign you a port on their server that forwards through to your container. This is important for qBittorrent — without an open port you can connect to peers but they can’t initiate connections to you, which kills your ratio on private trackers.
With VPN_PORT_FORWARDING=on, Gluetun writes the forwarded port to /gluetun/forwarded_port. You can read this file with a small script and push the port into qBittorrent’s API automatically.
# Check your current forwarded port
cat /mnt/tank/configs/gluetun/forwarded_port
Verifying it works
# Check Gluetun is connected
docker logs gluetun | grep -i "ip|connected|port"
# Check qBittorrent is using the VPN IP (not your home IP)
docker exec qbittorrent curl -s ifconfig.me
# Should return your VPN provider's IP, not your home IP
If the IP matches your VPN provider, everything is working. If it returns your home IP, Gluetun isn’t connected or the container isn’t using network_mode: "container:gluetun" correctly.

[…] Already read the Gluetun overview? This guide focuses specifically on the qBittorrent integration and getting port forwarding working end-to-end. If you want the broader picture of routing multiple containers through Gluetun, see the Gluetun overview post. […]