Gluetun: Route Docker Containers Through a VPN
Gluetun is a VPN client container that other Docker containers can route their traffic through. Instead of running a VPN on your entire server (which breaks self-hosted services that need direct connections), Gluetun lets you selectively route only the containers that need VPN access.
The typical use case: route your torrent client and *arr stack through a VPN while keeping Nextcloud, Jellyfin, and everything else on your regular connection.
Why Gluetun?
You could configure VPN connections inside individual containers, but Gluetun centralizes it:
- One VPN connection — Multiple containers share a single tunnel
- Kill switch — If the VPN drops, traffic stops. No IP leaks
- Provider support — Works with 30+ VPN providers out of the box
- Port forwarding — Supports port forwarding for providers that offer it
- Health checks — Built-in connectivity checks and auto-reconnect
- Firewall — Built-in firewall rules that only allow traffic through the VPN
The alternative — running OpenVPN or WireGuard inside each container — means managing multiple VPN connections, multiple configurations, and multiple potential leak points.
Installation
Basic Setup with Mullvad (WireGuard)
# docker-compose.yml
services:
gluetun:
image: qmcgaw/gluetun
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
ports:
# Ports for containers using gluetun's network
- "8080:8080" # qBittorrent web UI
- "6881:6881" # qBittorrent incoming connections
environment:
- VPN_SERVICE_PROVIDER=mullvad
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=your-private-key
- WIREGUARD_ADDRESSES=10.x.x.x/32
- SERVER_COUNTRIES=Switzerland
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: "service:gluetun" # Route through Gluetun
depends_on:
gluetun:
condition: service_healthy
environment:
- PUID=1000
- PGID=1000
- TZ=America/Los_Angeles
volumes:
- qbit_config:/config
- /data/downloads:/downloads
restart: unless-stopped
volumes:
qbit_config:
The key line is network_mode: "service:gluetun" — this makes qBittorrent use Gluetun's network stack. All of qBittorrent's traffic goes through the VPN tunnel.
Note that port mappings for containers using Gluetun's network go on the Gluetun container, not on the service container.
With ProtonVPN (OpenVPN)
services:
gluetun:
image: qmcgaw/gluetun
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
ports:
- "8080:8080"
environment:
- VPN_SERVICE_PROVIDER=protonvpn
- VPN_TYPE=openvpn
- OPENVPN_USER=your-openvpn-user
- OPENVPN_PASSWORD=your-openvpn-password
- SERVER_COUNTRIES=Netherlands
restart: unless-stopped
With a Custom WireGuard Configuration
If your provider isn't directly supported:
environment:
- VPN_SERVICE_PROVIDER=custom
- VPN_TYPE=wireguard
- VPN_ENDPOINT_IP=1.2.3.4
- VPN_ENDPOINT_PORT=51820
- WIREGUARD_PUBLIC_KEY=server-public-key
- WIREGUARD_PRIVATE_KEY=your-private-key
- WIREGUARD_ADDRESSES=10.x.x.x/32
Routing Multiple Containers
Add more containers to the VPN by setting their network mode:
services:
gluetun:
image: qmcgaw/gluetun
ports:
- "8080:8080" # qBittorrent
- "8989:8989" # Sonarr
- "7878:7878" # Radarr
- "9696:9696" # Prowlarr
# ... VPN config
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
network_mode: "service:gluetun"
depends_on:
gluetun:
condition: service_healthy
# ...
sonarr:
image: lscr.io/linuxserver/sonarr:latest
network_mode: "service:gluetun"
depends_on:
gluetun:
condition: service_healthy
# ...
radarr:
image: lscr.io/linuxserver/radarr:latest
network_mode: "service:gluetun"
depends_on:
gluetun:
condition: service_healthy
# ...
All four containers share the single VPN connection and appear with the same public IP.
Port Forwarding
Some VPN providers (Mullvad, ProtonVPN, AirVPN) support port forwarding through the VPN. This is important for torrent clients — without an incoming port, you can only connect to peers who are connectable, reducing speeds.
environment:
- VPN_PORT_FORWARDING=on
- VPN_PORT_FORWARDING_PROVIDER=protonvpn
Gluetun exposes the forwarded port via its API:
curl http://localhost:8000/v1/openvpn/portforwarded
You can configure your torrent client to use this port automatically.
Verifying the VPN
Check Your External IP
From inside a container using Gluetun's network:
docker exec qbittorrent curl -s ifconfig.me
This should show the VPN server's IP, not your real IP.
Check Gluetun's Status
Gluetun has a built-in HTTP control server:
# Check public IP
curl http://localhost:8000/v1/publicip/ip
# Check VPN status
curl http://localhost:8000/v1/openvpn/status
Health Check
Gluetun includes a health check that Docker can use:
docker inspect --format='{{.State.Health.Status}}' gluetun
The depends_on condition service_healthy in our compose file ensures containers only start after the VPN is connected.
The Kill Switch
Gluetun's firewall is a kill switch by default. If the VPN connection drops:
- All traffic from containers using Gluetun's network is blocked
- Gluetun attempts to reconnect
- Traffic resumes only after the VPN is back up
This prevents IP leaks — your real IP is never exposed through the containers, even during VPN reconnection.
Troubleshooting
Container Can't Reach Local Network
Containers using network_mode: "service:gluetun" can't access your local network by default (all traffic goes through the VPN). To allow local access:
environment:
- FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24
This is needed when Sonarr/Radarr need to reach Jellyfin or other services on your local network.
DNS Resolution Issues
Gluetun handles DNS through the VPN by default. If you have DNS issues:
environment:
- DOT=off
- DNS_ADDRESS=1.1.1.1
Slow Speeds
- Try a different VPN server (closer geographically)
- Switch from OpenVPN to WireGuard (significantly faster)
- Check if your VPN provider throttles certain traffic
- Verify your server has enough CPU for encryption (especially OpenVPN)
Gluetun vs Host-Level VPN vs Per-Container VPN
| Approach | Scope | Complexity | Leak Risk |
|---|---|---|---|
| Gluetun | Selected containers | Low | Very low |
| Host VPN | Entire server | Medium | Medium (split tunnel needed) |
| Per-container | Individual containers | High | Varies |
Host-Level VPN Problems
Running a VPN on the host means all traffic routes through it. This breaks:
- Services that need your real IP (Plex remote access, game servers)
- Services that peers connect to (Nextcloud, Jellyfin)
- Let's Encrypt certificate validation
- Reverse proxy connections
You'd need split tunneling, which is complex and error-prone.
Gluetun's Advantage
Only containers you explicitly route through Gluetun use the VPN. Everything else is untouched. Clean separation, minimal configuration.
Verdict
Gluetun solves the "I need a VPN for some things but not everything" problem cleanly. One container, one VPN connection, and any container that needs VPN access just sets network_mode: "service:gluetun". The built-in kill switch and health checks mean you can trust it to not leak your IP.
If you're running a torrent client, *arr stack, or any other services that benefit from VPN routing, Gluetun is the standard way to do it in Docker. It's one of those tools that the self-hosting community has broadly settled on as the right solution.