docker.recipes
Networking10 min read

Running Docker Services Through VPN

Route container traffic through VPN providers using Gluetun for privacy, geo-unblocking, and secure connections.

01Why Route Containers Through VPN?

Some self-hosted services benefit from VPN routing: **Use cases:** • **Torrenting**: Hide your IP from peers and trackers • **Geo-unblocking**: Access region-restricted content • **Privacy**: Prevent services from leaking your IP • **Split tunneling**: Only route specific containers **Gluetun** is the go-to solution—a lightweight container that connects to 60+ VPN providers and routes traffic for other containers through it.

Gluetun supports WireGuard and OpenVPN, with WireGuard being faster and more efficient.

02Basic Gluetun Setup

Gluetun connects to your VPN provider and exposes a network that other containers can use. Configuration varies by provider—check the Gluetun wiki for your specific provider. **Supported providers include:** Mullvad, NordVPN, Surfshark, ProtonVPN, PIA, Windscribe, and 60+ more. Here's a setup for Mullvad (WireGuard):
1services:
2 gluetun:
3 image: qmcgaw/gluetun:latest
4 container_name: gluetun
5 cap_add:
6 - NET_ADMIN
7 devices:
8 - /dev/net/tun:/dev/net/tun
9 environment:
10 - VPN_SERVICE_PROVIDER=mullvad
11 - VPN_TYPE=wireguard
12 - WIREGUARD_PRIVATE_KEY=${MULLVAD_PRIVATE_KEY}
13 - WIREGUARD_ADDRESSES=10.x.x.x/32
14 - SERVER_COUNTRIES=Sweden
15 volumes:
16 - ./gluetun:/gluetun
17 ports:
18 # Ports for containers using this VPN
19 - 8080:8080 # qBittorrent
20 - 6881:6881 # Torrent port
21 restart: unless-stopped

The NET_ADMIN capability is required for VPN functionality. This gives the container elevated network privileges.

03Routing Containers Through the VPN

To route a container's traffic through Gluetun, use `network_mode: service:gluetun`. This makes the container share Gluetun's network stack—all its traffic goes through the VPN. **Important:** When using network_mode, ports must be exposed on the Gluetun container, not the service itself.
1services:
2 gluetun:
3 image: qmcgaw/gluetun:latest
4 cap_add:
5 - NET_ADMIN
6 devices:
7 - /dev/net/tun:/dev/net/tun
8 environment:
9 - VPN_SERVICE_PROVIDER=mullvad
10 - VPN_TYPE=wireguard
11 - WIREGUARD_PRIVATE_KEY=${MULLVAD_PRIVATE_KEY}
12 - WIREGUARD_ADDRESSES=10.x.x.x/32
13 - SERVER_COUNTRIES=Sweden
14 ports:
15 - 8080:8080 # qBittorrent WebUI
16 - 9696:9696 # Prowlarr
17 restart: unless-stopped
18
19 qbittorrent:
20 image: linuxserver/qbittorrent:latest
21 container_name: qbittorrent
22 network_mode: service:gluetun
23 depends_on:
24 gluetun:
25 condition: service_healthy
26 environment:
27 - PUID=1000
28 - PGID=1000
29 volumes:
30 - ./qbittorrent:/config
31 - /downloads:/downloads
32 # NO ports here - they go on gluetun!
33 restart: unless-stopped
34
35 prowlarr:
36 image: linuxserver/prowlarr:latest
37 network_mode: service:gluetun
38 depends_on:
39 - gluetun
40 volumes:
41 - ./prowlarr:/config
42 restart: unless-stopped

Use depends_on with condition: service_healthy to ensure the VPN is connected before starting dependent containers.

04Kill Switch and Leak Prevention

Gluetun has a built-in kill switch—if the VPN disconnects, all traffic is blocked. This prevents IP leaks. You can also configure allowed IPs for local network access. **Kill switch behavior:** • Blocks all traffic if VPN disconnects • Prevents DNS leaks (uses VPN's DNS) • Can whitelist local network ranges **Verify your setup:** 1. Check your public IP from inside the container 2. Disconnect VPN and verify traffic is blocked 3. Test DNS to ensure no leaks
1# Check your IP from inside a VPN-routed container
2docker exec qbittorrent curl -s https://ipinfo.io/ip
3
4# Compare to your real IP
5curl -s https://ipinfo.io/ip
6
7# Check DNS (should show VPN provider's DNS)
8docker exec qbittorrent cat /etc/resolv.conf
9
10# Test kill switch: stop gluetun and try to connect
11docker stop gluetun
12docker exec qbittorrent curl -s https://ipinfo.io/ip
13# Should fail/timeout

05Split Tunneling: Selective VPN Routing

Not all services need VPN routing. Use split tunneling to route only specific containers through the VPN while others use your regular connection. **Architecture:** • Services needing privacy → network_mode: service:gluetun • Regular services → Normal Docker networking • Services talking to VPN'd containers → Shared network The key is understanding that VPN-routed containers can only communicate with containers in their same network stack.
1services:
2 gluetun:
3 image: qmcgaw/gluetun:latest
4 cap_add:
5 - NET_ADMIN
6 devices:
7 - /dev/net/tun:/dev/net/tun
8 environment:
9 - VPN_SERVICE_PROVIDER=mullvad
10 - VPN_TYPE=wireguard
11 - WIREGUARD_PRIVATE_KEY=${MULLVAD_PRIVATE_KEY}
12 - WIREGUARD_ADDRESSES=10.x.x.x/32
13 # Allow local network access
14 - FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24
15 ports:
16 - 8080:8080
17 networks:
18 - vpn_network
19 restart: unless-stopped
20
21 # Through VPN
22 qbittorrent:
23 image: linuxserver/qbittorrent:latest
24 network_mode: service:gluetun
25 depends_on:
26 - gluetun
27 volumes:
28 - ./qbittorrent:/config
29 - /downloads:/downloads
30
31 # NOT through VPN - normal networking
32 jellyfin:
33 image: jellyfin/jellyfin:latest
34 ports:
35 - 8096:8096
36 volumes:
37 - ./jellyfin:/config
38 - /media:/media:ro
39 networks:
40 - vpn_network # Can access gluetun services
41
42networks:
43 vpn_network:

Use FIREWALL_OUTBOUND_SUBNETS to allow VPN-routed containers to access your local network (like for NAS access).

06VPN Port Forwarding for Better Speeds

Some VPN providers support port forwarding, which improves torrent speeds by allowing incoming connections. Gluetun can automatically configure this. **Providers with port forwarding:** • ProtonVPN (paid plans) • Private Internet Access • Mullvad (limited) • AirVPN When enabled, Gluetun requests a forwarded port and writes it to a file or updates container environment variables.
1services:
2 gluetun:
3 image: qmcgaw/gluetun:latest
4 cap_add:
5 - NET_ADMIN
6 devices:
7 - /dev/net/tun:/dev/net/tun
8 environment:
9 - VPN_SERVICE_PROVIDER=private internet access
10 - VPN_TYPE=openvpn
11 - OPENVPN_USER=${PIA_USER}
12 - OPENVPN_PASSWORD=${PIA_PASS}
13 - SERVER_REGIONS=Netherlands
14 # Enable port forwarding
15 - VPN_PORT_FORWARDING=on
16 - VPN_PORT_FORWARDING_PROVIDER=protonvpn
17 volumes:
18 - ./gluetun:/gluetun
19 ports:
20 - 8080:8080
21 restart: unless-stopped
22
23# The forwarded port is available at:
24# /gluetun/forwarded_port
25# Or via the control server: http://localhost:8000/v1/openvpn/portforwarded

07Troubleshooting VPN Containers

Common issues and solutions when running containers through VPN: **Container can't connect to internet:** • Check Gluetun logs for VPN connection errors • Verify credentials and server settings • Ensure /dev/net/tun device exists **Can't access container WebUI:** • Ports must be on Gluetun, not the service • Check firewall rules in Gluetun **Slow speeds:** • Try a different server/country • Use WireGuard instead of OpenVPN • Enable port forwarding if available **Container starts before VPN:** • Use depends_on with health check condition
1# Check Gluetun status
2docker logs gluetun
3
4# Check if VPN is connected
5docker exec gluetun curl -s https://ipinfo.io
6
7# Test connectivity from a routed container
8docker exec qbittorrent ping -c 3 google.com
9
10# Check Gluetun health
11curl http://localhost:8000/v1/vpn/status
12
13# Restart with fresh connection
14docker compose restart gluetun

If Gluetun fails to connect, dependent containers will have no network access. Always use health checks and monitoring.