WireGuard (NordLynx) client docker container that routes other containers' traffic through NordVPN servers automatically.
Prefer OpenVPN? This has a sibling project, azinchen/nordvpn — the same auto-routing NordVPN container over OpenVPN (including XOR traffic obfuscation). Both share the same configuration model and feature set.
--net=container:vpnNETWORK (details)DNS (details)wireguard-go when the kernel lacks WireGuard (details)FORWARD_FROM (details)📖 Full documentation on the Wiki — configuration guides, examples, troubleshooting, FAQ, and architecture.
docker run -d --name vpn \
--cap-add=NET_ADMIN \
--sysctl net.ipv4.conf.all.src_valid_mark=1 \
-e TOKEN=your_nordvpn_token_here \
azinchen/nordvpn-wg
Route other containers through VPN:
docker run --net=container:vpn -d your/application
Also available from GitHub Container Registry: ghcr.io/azinchen/nordvpn-wg
--cap-add=NET_ADMIN and --sysctl net.ipv4.conf.all.src_valid_mark=1 (or privileged: true)wireguard module loaded on the host. On kernels without the module, the container automatically falls back to userspace WireGuard (wireguard-go); that fallback additionally requires --device /dev/net/tun (not needed when the kernel module is available) and is slower than kernel WireGuard.TOKEN — the container reads your NordLynx (WireGuard) key from the NordVPN API on every connectNote: The token is different from your regular NordVPN login. It is used only to fetch your WireGuard key; the key lives only in the container-private WireGuard config (root-owned, mode 0600), is removed when the service stops, and is re-fetched on every connect — it is never logged and never stored in the image or a volume. See the wiki for the API one-liner if you prefer to inspect it yourself.
services:
vpn:
image: azinchen/nordvpn-wg:latest
cap_add:
- NET_ADMIN
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
environment:
- TOKEN=your_nordvpn_token_here
- COUNTRY=United States;CA
- RANDOM_TOP=10
- RECREATE_VPN_CRON=0 */6 * * *
- NETWORK=192.168.1.0/24
ports:
- "8080:80" # host:container — use your app's listening port
restart: unless-stopped
app:
image: nginx:alpine
network_mode: "service:vpn"
depends_on:
- vpn
restart: unless-stopped
More examples: Docker Compose · Docker Run
List values (countries, cities, CIDRs, URLs, IPs) accept
;or,as separators; whitespace around separators is ignored.
NordVPN access token — see Getting a Token above.
| Variable | Details |
|---|---|
| TOKEN | Required — NordVPN access token; used to fetch your WireGuard key from the API. |
Pick which servers to connect to; filters combine to narrow the pool. See Server Selection.
| Variable | Details |
|---|---|
| COUNTRY | Filter by countries: names, codes, IDs, or server hostnames (list). |
| CITY | Filter by cities: names, IDs, or server hostnames (list). |
| GROUP | Filter by server group (list, details). |
| RANDOM_TOP | Randomize top N servers. Default: 0 |
DNS resolution through the tunnel. See Custom DNS.
| Variable | Details |
|---|---|
| DNS | DNS servers written to resolv.conf (resolution goes through the tunnel). Default: 103.86.96.100;103.86.99.100 |
Rotate servers on a schedule and verify the tunnel actually works. See Automatic Reconnection.
| Variable | Details |
|---|---|
| RECREATE_VPN_CRON | Server switching schedule (cron). Default: disabled |
| CHECK_CONNECTION_CRON | Health monitoring schedule (cron). Default: disabled |
| CHECK_CONNECTION_URL | URLs to test connectivity. Default: https://www.google.com |
| CHECK_CONNECTION_ATTEMPTS | Connection test retry count. Default: 5 |
| CHECK_CONNECTION_ATTEMPT_INTERVAL | Seconds between retries. Default: 10 |
| HEALTHCHECK_ENABLED | Enable the Docker HEALTHCHECK probe (checks wg0 + connectivity via CHECK_CONNECTION_URL). When false, the container always reports healthy. Default: false |
Open the kill‑switch firewall for LAN access and downstream routing. See Local Network Access and VPN Gateway Mode.
| Variable | Details |
|---|---|
| NETWORK | LAN/inter‑container CIDRs to allow. Default: none |
| FORWARD_FROM | Downstream CIDRs allowed to route OUT through the tunnel (gateway mode). Traffic must arrive already SNATed into these nets. Default: none |
| GATEWAY_DNS | DNS interception for FORWARD_FROM clients: redirect (DNAT port 53 to the VPN resolvers from DNS, through the tunnel), local (DNAT port 53 to this container, for a co‑located resolver such as AdGuard Home), forward (DNAT port 53 to GATEWAY_DNS_SERVER, reached directly over the uplink — not through the tunnel), off. Default: off |
| GATEWAY_DNS_SERVER | External IPv4 resolver(s) for GATEWAY_DNS=forward (e.g. an AdGuard Home on your LAN). With a list, the first resolver answering a DNS probe at startup is used. Default: none |
| TTL_SET | Rewrite the TTL of traffic leaving through the tunnel (e.g. 64); normalizes the value the VPN exit sees and hides every hop behind this container from client traceroutes (details). Integer 1–255. Default: unset |
| TTL_INC | Increment the TTL of traffic arriving on eth0 (e.g. 1), cancelling this container's own decrement so it disappears from client traceroutes (details). Needs FORWARD_FROM. Integer 1–255. Default: unset |
Low‑level settings; the defaults work for most setups.
| Variable | Details |
|---|---|
| NORDVPNAPI_IP | API bootstrap IPs. Default: 104.16.208.203;104.19.159.190 |
| NETWORK_DIAGNOSTIC_ENABLED | Enable network diagnostics on connect (details). Default: false |
| ALLOW_MISSING_IPTABLES_RULES | Tolerate failures applying wg-quick's anti-leak iptables rules — needed on hosts whose kernel lacks the required netfilter modules (e.g. Synology DSM), where the tunnel would otherwise be torn down. The container's own default-DROP kill switch stays active. See Firewall Backends. Default: false |
If you have any problems with or questions about this image, please contact me through a GitHub issue or email.
Check the Troubleshooting and FAQ wiki pages first.
Content type
Image
Digest
sha256:5b0eb3820…
Size
16.2 MB
Last updated
8 days ago
docker pull azinchen/nordvpn-wg