Gluetun VPN Proxy¶
Gluetun provides Docker-managed VPN proxies supporting 50+ VPN providers.
Prerequisites¶
Docker must be installed and running.
# Linux
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER # Then log out/in
# Windows/Mac
# Install Docker Desktop: https://www.docker.com/products/docker-desktop/
Quick Start¶
1. Configuration¶
Add to ~/.config/unshackle/unshackle.yaml:
proxy_providers:
gluetun:
providers:
windscribe:
vpn_type: openvpn
credentials:
username: "YOUR_OPENVPN_USERNAME"
password: "YOUR_OPENVPN_PASSWORD"
2. Usage¶
Use 2-letter country codes directly:
unshackle dl SERVICE CONTENT --proxy gluetun:windscribe:us
unshackle dl SERVICE CONTENT --proxy gluetun:windscribe:uk
Format: gluetun:provider:region
Provider Credential Requirements¶
OpenVPN (Recommended): Most providers support OpenVPN with just username and password - the simplest setup.
WireGuard: Requires private keys and varies by provider. See the Gluetun Wiki for provider-specific requirements. Note that vpn_type defaults to wireguard if not specified.
Getting Your Credentials¶
Windscribe (OpenVPN)¶
- Go to windscribe.com/getconfig/openvpn
- Log in with your Windscribe account
- Select any location and click "Get Config"
- Copy the username and password shown
NordVPN (OpenVPN)¶
- Go to NordVPN Service Credentials
- Log in with your NordVPN account
- Generate or view your service credentials
- Copy the username and password
Note
Use service credentials, NOT your account email/password.
WireGuard Credentials (Advanced)¶
WireGuard requires private keys instead of username/password. See the Gluetun Wiki for provider-specific WireGuard setup.
Configuration Examples¶
OpenVPN (Recommended)
Most providers support OpenVPN with just username and password:
providers:
windscribe:
vpn_type: openvpn
credentials:
username: YOUR_OPENVPN_USERNAME
password: YOUR_OPENVPN_PASSWORD
nordvpn:
vpn_type: openvpn
credentials:
username: YOUR_SERVICE_USERNAME
password: YOUR_SERVICE_PASSWORD
WireGuard (Advanced)
WireGuard can be faster but requires more complex credential setup:
# NordVPN/ProtonVPN (only private_key needed)
providers:
nordvpn:
vpn_type: wireguard
credentials:
private_key: YOUR_PRIVATE_KEY
# Surfshark/Mullvad/IVPN (private_key AND addresses required)
surfshark:
vpn_type: wireguard
credentials:
private_key: YOUR_PRIVATE_KEY
addresses: 10.x.x.x/32
# Windscribe (all three credentials required)
windscribe:
vpn_type: wireguard
credentials:
private_key: YOUR_PRIVATE_KEY
addresses: 10.x.x.x/32
preshared_key: YOUR_PRESHARED_KEY
Server Selection¶
Most providers use SERVER_COUNTRIES, but some use SERVER_REGIONS:
| Variable | Providers |
|---|---|
SERVER_COUNTRIES |
NordVPN, ProtonVPN, Surfshark, Mullvad, ExpressVPN, and most others |
SERVER_REGIONS |
Windscribe, VyprVPN, VPN Secure |
Unshackle handles this automatically - just use 2-letter country codes.
Per-Provider Server Mapping¶
You can explicitly map region codes to country names, cities, or hostnames per provider:
providers:
nordvpn:
vpn_type: openvpn
credentials:
username: YOUR_USERNAME
password: YOUR_PASSWORD
server_countries:
us: "United States"
uk: "United Kingdom"
server_cities:
us: "New York"
server_hostnames:
us: "us1239.nordvpn.com"
Specific Server Selection¶
Use a <country><number> region (e.g. us1239) to target a specific server. Unshackle builds the
hostname automatically per provider:
| Provider | Hostname format |
|---|---|
| NordVPN | us1239.nordvpn.com |
| Surfshark | us-1239.prod.surfshark.com |
| ExpressVPN | us-1239.expressvpn.com |
| CyberGhost | us-s1239.cg-dialup.net |
| Other | us1239 (passed as-is to SERVER_HOSTNAMES) |
Extra Environment Variables¶
You can pass additional Gluetun environment variables per provider using extra_env:
providers:
nordvpn:
vpn_type: openvpn
credentials:
username: YOUR_USERNAME
password: YOUR_PASSWORD
extra_env:
LOG_LEVEL: debug
Global Settings¶
proxy_providers:
gluetun:
providers: {...}
base_port: 8888 # Starting port (default: 8888)
auto_cleanup: true # Remove containers on exit (default: true)
verify_ip: true # Verify IP matches region (default: true)
container_prefix: "unshackle-gluetun" # Docker container name prefix (default: "unshackle-gluetun")
auth_user: username # Proxy auth (optional)
auth_password: password # Proxy auth (optional)
Features¶
- Container Reuse: First request takes 10-30s; subsequent requests are instant. Containers created by other unshackle processes are auto-detected via
docker inspectand reused. - Ready Detection: Waits up to 60s for both the HTTP proxy to listen (
[http proxy] listening) and the VPN tunnel to come up (initialization sequence completedorpublic ip address is) before returning the proxy URI. Bails early onfatalorinvalid credentialslog lines. - IP Verification: When
verify_ip: true(default), looks up the exit IP viaipinfo.iothrough the proxy and compares country code to the requested region. Retries 3 times with exponential backoff (1s, 2s, 4s). - Concurrent Sessions: Multiple downloads share the same container; ports are allocated thread-safely starting at
base_port. - Specific Servers: Use
--proxy gluetun:nordvpn:us1239for specific server selection (see table above). - Automatic Image Pull: The Gluetun Docker image (
qmcgaw/gluetun:latest) is pulled automatically on first use (5 min timeout). - Secure Credentials: Credentials are passed via temporary env files (mode 0600), then zero-overwritten and unlinked after
docker run. They never appear in process listings. - Auto Cleanup: Containers are removed via
atexit(Ctrl+C still works normally). Disable withauto_cleanup: falseto leave them stopped instead.
Container Management¶
# View containers
docker ps | grep unshackle-gluetun
# Check logs
docker logs unshackle-gluetun-nordvpn-us
# Remove all containers
docker ps -a | grep unshackle-gluetun | awk '{print $1}' | xargs docker rm -f
Troubleshooting¶
Docker Permission Denied (Linux)¶
VPN Connection Failed¶
Check container logs for specific errors:
Common issues:
- Invalid/missing credentials
- Windscribe WireGuard requires preshared_key (can be empty string, but must be set in credentials)
- VPN provider server issues
- Container startup timeout (default 60 seconds)
Resources¶
- Gluetun Wiki - Official provider documentation
- Gluetun GitHub
ExpressVPN HTTPS Proxy¶
ExpressVPN is a standalone HTTPS proxy provider — it does not use Docker or the gluetun sub-provider system. It authenticates via a browser-extension OAuth flow (Keycloak/PKCE) and returns an authenticated https://cat:<token>@<host>:443 proxy URL.
Prerequisites¶
An active ExpressVPN subscription is required. The provider resolves proxy-capable locations through ExpressVPN's own API, so no local VPN client or Docker container is needed.
Authentication¶
The provider uses a token cascade (tried in order):
- Cached tokens —
cache/global/expressvpn_tokens.json(auto-written after first auth; access and refresh tokens are refreshed automatically when they expire). - Exported browser cookies —
cookies/vpn/expressvpn.txt(also accepted atcookies/vpns/expressvpn.txt). Must contain theKEYCLOAK_IDENTITYandKEYCLOAK_SESSIONcookies fromauth.expressvpn.com. Used for a one-time PKCE bootstrap that populates the token cache. - Desktop
account.json— configured via theaccount_jsonkey (see below).
Export cookies from a browser that is logged in to auth.expressvpn.com. Any Netscape-format, JSON array, or name=value file is accepted.
Configuration¶
Add to ~/.config/unshackle/unshackle.yaml under proxy_providers:
proxy_providers:
expressvpn:
region_map:
us: "ny" # --proxy expressvpn:us → New York (default city)
gb: "london" # --proxy expressvpn:gb → London
de: "frankfurt" # --proxy expressvpn:de → Frankfurt
es: "madrid" # --proxy expressvpn:es → Madrid
mx: # --proxy expressvpn:mx → smart/random MX location
region_map maps a country code to a default city preset. An empty (null) value enables smart connection (a random location in that country). The preset is only applied when the CLI query contains no city; an explicit city in the query always takes precedence.
Optional keys¶
| Key | Default | Description |
|---|---|---|
region_map |
{} |
Country → default city presets (see above) |
server_map |
{} |
Alias → ExpressVPN location slug or direct hostname |
cookie_path |
cookies/vpn/expressvpn.txt |
Path to exported browser cookies |
cache_path |
cache/global/expressvpn_tokens.json |
Token cache location |
account_json |
(none) | Path to ExpressVPN desktop account.json |
refresh_token |
(none) | OAuth refresh token (manual override) |
timeout |
10.0 |
HTTP request timeout in seconds |
Usage¶
unshackle dl SERVICE CONTENT --proxy expressvpn:us # random/smart US location
unshackle dl SERVICE CONTENT --proxy expressvpn:us-ny # specific city (New York)
unshackle dl SERVICE CONTENT --proxy expressvpn:us-ny-2 # pinned server #2 in New York
unshackle dl SERVICE CONTENT --proxy expressvpn:mx # random/smart Mexico location
unshackle dl SERVICE CONTENT --proxy expressvpn:es-madrid # specific city (Madrid)
Query format: expressvpn:<country>[-<city>[-<N>]]
country— 2-letter country code (e.g.us,gb,de).city— optional city abbreviation or slug matched against location names using first-letter abbreviation (ny→ "New York"), exact slug, prefix, then substring strategies.N— optional 1-based server index (e.g.-2selects the second server in the matched city). IfNexceeds the available server count, a random server is selected.
City matching¶
The city part of the query is matched against ExpressVPN location names using these strategies (in priority order):
- First-letter abbreviation —
nymatches "New York" - Exact slug —
miamimatches "Miami" - Prefix match —
miamatches "Miami" - Substring match —
yorkmatches "New York"
Token cache¶
After the first successful authentication the token cache is written to cache/global/expressvpn_tokens.json (mode 0600). Subsequent runs refresh expired access/SRT/connection tokens automatically without re-reading cookies.