boingbasti/nordvpn-gateway

By boingbasti

β€’Updated 4 days ago

Secure NordVPN gateway container with auto reconnect and killswitch.

Image
Networking
1

4.6K

boingbasti/nordvpn-gateway repository overview

GitHub Release GitHub Release Date Docker Pulls Image Size

β πŸ›‘οΈ NordVPN Gateway Container

ℹ️ Under the hood: Based on NordVPN Linux Client 4.6.0

A stable, self-healing, and intelligent Docker container that turns your host into a secure NordVPN gateway for other containers and your entire LAN. It supports WireGuard server integration, DNS over VPN via AdGuard Home, and SOCKS5 / HTTP proxies, all routed through NordVPN.


⁠✨ Core Features

  • πŸ”’ Secure & Self-Healing β€” Uses token authentication (via Docker Secret) and the built-in killswitch. A persistent loop monitors the connection and daemon health.
  • 🎯 Quality Ping & Smart Selection β€” VPN_AUTO_CONNECT=best sends burst pings (0.2s interval) to recommended servers. It strictly filters for 0% packet loss and selects the target with the lowest average latency.
  • πŸ”„ Smart Candidate Rotation β€” If the connected server fails the speed test (latency vs. load mismatch), the container automatically rotates to the next best "candidate" (2nd or 3rd best ping) without getting stuck in a reconnect loop.
  • ⚑ Fail Fast β€” Performs an immediate speed check ~15 seconds after connection to ensure the chosen server performs well. If not, it rotates immediately.
  • πŸ“ˆ Gigabit-Ready Speed Tests β€” Optional support for 100MB test files (SPEED_TEST_URL) to accurately measure high-speed lines where TCP slow-start distorts results.
  • πŸš€ Adaptive MTU β€” Fast binary-search MTU detection and TCP MSS clamping keep throughput stable.
  • 🧩 WireGuard Bypass Mode β€” Lets a local WireGuard server (e.g. wg‑easy) handshake and route through the VPN without the killswitch blocking it. Requires macvlan.
  • 🧭 DNS Stability β€” Inside the gateway namespace, NordVPN DNS (103.86.96.100) is enforced to prevent leaks.

β πŸ”Œ Exposed Services & Ports

Since all services share the VPN network stack, they are accessible via the Gateway IP (not localhost).

ServicePortDescriptionUsage Example
SOCKS5 Proxy1080Secure proxy for browsers/apps.curl -x socks5h://GATEWAY_IP:1080 ipinfo.io
HTTP Proxy8118Privoxy HTTP proxy with ad-blocking.curl -x http://GATEWAY_IP:8118 ipinfo.io
AdGuard Home80 / 3000DNS Server & Web Interface.Open http://GATEWAY_IP:80 (or 3000 for setup)
WireGuard51820UDP Port for VPN Clients (wg-easy).Configure in Router Port Forwarding.
wg-easy UI51821WireGuard Web Admin UI.Open http://WG_SERVER_IP:51821 (Note: Has its own IP)

β πŸ›  Web-Configurator for Automatic YAML

Instead of manually writing complex YAML, you can use the visual generator: πŸ‘‰ boingbasti/nordvpn-gateway-configurator⁠

It provides: βœ” UI selection of gateway mode (Simple vs. Advanced) βœ” Gigabit Support (100MB Speedtest option) βœ” Dual Routing Hooks (LAN Access vs. Strict Isolation) βœ” Automatic calculation of routing rules

Run locally:

docker run -d \
  --name nordvpn-config-gen \
  -p 8080:80 \
  boingbasti/nordvpn-gateway-configurator:latest

Then visit: http://localhost:8080⁠


β πŸš€ Usage Examples

⁠1. Secure Proxy (Simple)

Creates a SOCKS5 proxy routed fully through the VPN. No special network setup required.

services:
  vpn:
    image: boingbasti/nordvpn-gateway:latest
    container_name: nordvpn
    cap_add:
      - NET_ADMIN
      - NET_RAW
    devices:
      - /dev/net/tun
    volumes:
      - ./nordvpn_token.txt:/run/secrets/nordvpn_token:ro
      - /etc/localtime:/etc/localtime:ro
    environment:
      - VPN_TECHNOLOGY=NordLynx
      - VPN_COUNTRY=Germany
      - VPN_AUTO_CONNECT=best
      - KILLSWITCH=on
    restart: unless-stopped

  socks5:
    image: boingbasti/nordvpn-socks5:latest
    container_name: nordvpn-socks5
    network_mode: "service:vpn"
    depends_on:
      - vpn
    environment:
      - PROXY_PORT=1080
      - ALLOWED_IPS=192.168.1.0/24
    restart: unless-stopped

⁠2. Standalone LAN Gateway (macvlan)

1. Create the network once:

docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth0 \
  vpn_gateway_net

2. Compose:

services:
  vpn:
    image: boingbasti/nordvpn-gateway:latest
    container_name: nordvpn-gateway
    networks:
      vpn_gateway_net:
        ipv4_address: 192.168.1.100
    cap_add:
      - NET_ADMIN
      - NET_RAW
    devices:
      - /dev/net/tun
    volumes:
      - ./nordvpn_token.txt:/run/secrets/nordvpn_token:ro
      - /etc/localtime:/etc/localtime:ro
    environment:
      - VPN_COUNTRY=Germany
      - VPN_AUTO_CONNECT=best
      - KILLSWITCH=on
      - ALLOWLIST_SUBNET=192.168.1.0/24
      - VPN_MTU=auto
    sysctls:
      - net.ipv4.ip_forward=1
    restart: unless-stopped

networks:
  vpn_gateway_net:
    external: true

⁠3. Full Gateway Stack (Advanced)

Includes: Gateway + WireGuard Server (wg-easy) + AdGuard Home + Proxies.

services:
  vpn:
    image: boingbasti/nordvpn-gateway:latest
    container_name: nordvpn
    networks:
      vpn_gateway_net:
        ipv4_address: 192.168.1.100
    stop_grace_period: 45s
    cap_add: [NET_ADMIN, NET_RAW]
    devices: [/dev/net/tun]
    volumes:
      - ./nordvpn_token.txt:/run/secrets/nordvpn_token:ro
      - /etc/localtime:/etc/localtime:ro
    environment:
      - VPN_COUNTRY=Germany
      - VPN_AUTO_CONNECT=best
      - KILLSWITCH=on
      - ALLOWLIST_SUBNET=192.168.1.0/24,10.10.10.0/24
      - VPN_MTU=auto
      # WireGuard Bypass
      - WIREGUARD_BYPASS=on
      - WIREGUARD_SERVER_IP=192.168.1.200
      - WIREGUARD_SUBNET=10.10.10.0/24
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv6.conf.all.disable_ipv6=1
      - net.ipv6.conf.default.disable_ipv6=1
    restart: unless-stopped

  wg-easy:
    image: ghcr.io/wg-easy/wg-easy:15
    container_name: wg-easy
    networks:
      vpn_gateway_net:
        ipv4_address: 192.168.1.200
    depends_on: [vpn]
    cap_add: [NET_ADMIN, SYS_MODULE]
    volumes:
      - ./wg-easy-config:/etc/wireguard
      - /lib/modules:/lib/modules:ro
    environment:
      - DISABLE_IPV6=true
      - INSECURE=true
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=1
    restart: unless-stopped

  socks5:
    image: boingbasti/nordvpn-socks5:latest
    network_mode: "service:vpn"
    depends_on: [vpn]
    environment:
      - PROXY_PORT=1080
      - ALLOWED_IPS=192.168.1.0/24
    restart: unless-stopped

  privoxy:
    image: boingbasti/nordvpn-privoxy:latest
    network_mode: "service:vpn"
    depends_on: [vpn]
    restart: unless-stopped

  adguardhome:
    image: adguard/adguardhome:latest
    network_mode: "service:vpn"
    depends_on: [vpn]
    volumes:
      - ./adguard-work:/opt/adguardhome/work
      - ./adguard-config:/opt/adguardhome/conf
    restart: unless-stopped

networks:
  vpn_gateway_net:
    external: true

β βš™οΈ Environment Variables

⁠1) Basic VPN Connection
VariableDefaultDescription
NORDVPN_TOKENrequiredAuth token (use secret mount if possible).
VPN_COUNTRYGermanyTarget region.
VPN_GROUPp2pServer group. Use standard for normal servers.
VPN_SERVER(unset)Specific server (e.g. de1234). Overrides group.
VPN_TECHNOLOGYNordLynxNordLynx or OpenVPN.
PROTOCOL(unset)udp / tcp (OpenVPN only).
CONNECT_TIMEOUT60Connection timeout in seconds.
⁠2) Gateway & Routing
VariableDefaultExampleDescription
ALLOWLIST_SUBNET(unset)192.168.1.0/24,10.10.10.0/24Subnets allowed to route through VPN.
VPN_MTUauto1360Auto binary MTU detection or fixed value.
⁠3) Security & Ad-Blocking
VariableDefaultExampleDescription
THREAT_PROTECTION_LITEoffonEnables DNS-based blocking of ads & threats.
KILLSWITCHononDrop all non-VPN traffic to prevent leaks.
LAN_DISCOVERYoffonAllow access to local network devices while killswitch is active. Alternative to ALLOWLIST_SUBNET for simple setups.
POST_QUANTUMonoffEnable/disable post-quantum encryption support.
⁠4) WireGuard Bypass (macvlan only)
VariableDefaultExampleDescription
WIREGUARD_BYPASSoffonEnable routing exception for WG handshake.
WIREGUARD_SERVER_IP(unset)192.168.179.229LAN IP of WireGuard server.
WIREGUARD_SUBNET(unset)10.10.10.0/24Client subnet behind wg-easy.
SHOW_WGHOOKSoffonDisplay suggested PostUp/PostDown hooks.
⁠5) Performance, Health‑Checks & Reconnect
VariableDefaultExampleDescription
VPN_AUTO_CONNECToffbestSelect best server by latency & quality.
VPN_BEST_SERVER_CHECK_INTERVAL3015Minutes between best‑server refresh.
VPN_SPEED_CHECK_INTERVAL060Minutes between throughput checks.
VPN_MIN_SPEED520Minimum Mbit/s before rotating to next candidate.
SPEED_TEST_URLhttp://cachefly.cachefly.net/10mb.testhttp://cachefly.../100mb.testURL for speed tests. Use 100MB file for Gigabit lines.
CHECK_INTERVAL6030Loop check frequency (seconds).
RETRY_COUNT23Retry attempts before reconnect.
RETRY_DELAY22Seconds between retries.
VPN_REFRESH01440Forces periodic reconnect to rotate public IP.
⁠6) Logging & Diagnostics
VariableDefaultExampleDescription
LOG_STATUS_INTERVAL060Minutes between status logs (0=disabled).
DEBUGoffonEnable verbose logging.

β πŸ” Troubleshooting

  • Gigabit Speed Issues β†’ If your line is >250 Mbit, standard speed tests (10MB) are too small. Set SPEED_TEST_URL to a 100MB file (e.g., Cachefly or Hetzner) and increase VPN_SPEED_CHECK_INTERVAL to 60 min to save traffic.
  • Laggy Connection β†’ Try VPN_AUTO_CONNECT=off. This lets NordVPN load-balancing decide instead of relying purely on ping.
  • Container won’t start / macvlan errors β†’ Verify parent interface and recreate network with -o parent=eth0 adjusted to your host.
  • WG handshake blocked β†’ Enable WIREGUARD_BYPASS=on and verify WIREGUARD_SERVER_IP.

⁠Main Gateway Project
⁠Config Generator

⁠License

MIT

Tag summary

Content type

Image

Digest

sha256:a8ec3bfe8…

Size

98.1 MB

Last updated

4 days ago

docker pull boingbasti/nordvpn-gateway