WireGuard VPN node in Docker: zero-config peers, per-peer exits (direct, upstream VPN containers, Wi
310
A WireGuard node in a small self-configuring Docker container: a server for your devices with zero-config peer provisioning (keys, addresses, configs and QR codes generated for you), per-peer exits β straight out, through an upstream VPN container, through a WireGuard provider, or through another peer such as a home router β a fail-closed nftables kill switch on every path, and hot reload of everything without dropping tunnels. The same image is a WireGuard sidecar for other containers.
Part of a family. azinchen/ocserv-serverβ (OpenConnect server) shares this image's gateway-mode contract; azinchen/nordvpnβ and azinchen/nordvpn-wgβ plug in as upstream gateways; azinchen/openconnect-clientβ is the OpenConnect sidecar counterpart.
PEERS=alice,bob generates keys, addresses, configs and QR codes (detailsβ )exit=true makes a peer such as a home router an exit; config generated (detailsβ )wg-reload applies the peers file in place, unchanged peers keep sessions (detailsβ )LOCAL_GATEWAY tunnels the container and network_mode: service: clients (detailsβ )HEALTHCHECK for the interface, routing tables and egress probes (detailsβ )peer-report shows handshake, traffic and exit per peer (detailsβ )wireguard-go (detailsβ )π Full documentation on the Wikiβ β setup, peers, gateway mode, exit nodes, sidecar mode, configuration reference, troubleshooting, architecture.
# docker-compose.yml
services:
wireguard:
image: azinchen/wireguard:latest
container_name: wireguard
restart: unless-stopped
cap_add:
- NET_ADMIN
sysctls:
- net.ipv4.ip_forward=1
ports:
- 51820:51820/udp
environment:
- SERVER_ENDPOINT=vpn.example.com # what your devices dial
- PEERS=alice,bob
volumes:
- ./volumes/config:/etc/wireguard
docker compose up -d
docker exec wireguard peer-conf alice --qr # scan with the WireGuard app
docker exec wireguard peer-conf bob > bob.conf # or import the file
docker exec wireguard peer-report # who is connected, through which exit
| I want to⦠| Guide |
|---|---|
| Run a plain WireGuard server for my devices | Getting Startedβ Β· Peersβ |
| Send some peers out through NordVPN / another VPN container | Gateway Modeβ |
| Use a WireGuard provider (Mullvad, Proton, my VPS) as an exit | Exit Nodes and Upstreamsβ |
| Route a peer's traffic out through my home router (behind NAT) | Exit Nodes and Upstreamsβ |
| Route other containers or LAN hosts through a WireGuard tunnel | Sidecar Modeβ |
| Chain two of these containers (cascade) | Exit Nodes and Upstreams#cascadesβ |
Per-peer exits are one file:
# volumes/config/peers.conf
alice gw=nl # out through the nordvpn-wg container "nl"
bob # direct (default)
carol gw=home # out through the home router
home exit=true # the home router: dial-in exit node, everything generated
site-a allowed=192.168.50.0/24 # a site behind the peer, reachable from the VPN
with VPN_GATEWAYS=nl=nordvpn-wg in the environment (a DNS name on the Docker network). docker exec wireguard peer-conf home prints the config the router imports.
Grouped by feature; every variable is one line here β the Configuration Referenceβ has the full descriptions.
| Variable | Default | Description |
|---|---|---|
VPN_SUBNET | 10.10.10.0/24 | Peer subnet; the server takes the first host, peers are allocated from the second. |
LISTEN_PORT | 51820 | UDP port wg0 listens on. |
SERVER_ENDPOINT | (unset) | host[:port] your devices dial; written into generated configs (placeholder + warning if unset). |
WAN_IF | (auto) | Egress interface; auto-detected from the default route. |
MTU | 1420 | wg0 MTU. |
MSS | (unset) | Hard TCP MSS cap for forwarded traffic (default: clamp to interface MTU). |
TTL_SET | (unset) | Rewrite TTL/hop-limit of forwarded traffic on egress (e.g. 64); hides hops behind this node from traceroutes. |
PEER_TO_PEER | true | Allow peers to reach each other through wg0 (peer-to-peer traffic never enters a gateway). |
| Variable | Default | Description |
|---|---|---|
PEERS | (unset) | Inline list: alice,bob (keys generated) or bob=<pubkey> (device-made key). |
PEERS_FILE | /etc/wireguard/peers.conf | Peer definitions, one name [option=value ...] per line; file wins over PEERS. |
PEERS_WATCH | 0 | 1 = wg-reload automatically whenever the peers file changes. |
PEER_DNS | 1.1.1.1,1.0.0.1 | DNS = written into device configs (per-peer dns=). |
PEER_ROUTES | 0.0.0.0/0,::/0 | Device-side AllowedIPs (full tunnel); per-peer routes= for split tunnels. |
PEER_KEEPALIVE | 25 | PersistentKeepalive in device configs (0 = off). |
| Variable | Default | Description |
|---|---|---|
IPV6_FORWARD | 1 | Enable IPv6 forwarding inside the container. |
IPV6_NAT | 0 | NAT66 for IPV6_SUBNET; peers only get an IPv6 address when IPv6 is actually routed. |
IPV6_SUBNET | fda9:β¦::/64 | ULA peer subnet (<prefix>::/64). |
| Variable | Default | Description |
|---|---|---|
VPN_GATEWAY | (unset) | Default exit for unmapped peers: IP/DNS name of an upstream container, direct, block, or wg:<name> (an exit node / upstream). |
VPN_GATEWAY6 | (unset) | Same for IPv6 (block by default β no leak). |
VPN_GATEWAYS | (unset) | Named gateways, e.g. nl=nordvpn-wg,us=172.28.0.4,mullvad=wg. |
VPN_GATEWAYS6 | (unset) | Optional IPv6 per gateway name. |
VPN_GATEWAYS_FILE | (unset) | Gateways in a file (name ipv4 [ipv6]). |
VPN_GATEWAYS_RESOLVE_INTERVAL | 0 | Re-resolve DNS-named gateways and dial-out endpoints every N seconds, re-routing moved addresses in place. |
VPN_PEER_GATEWAY | (unset) | Peer β gateway map, e.g. alice=nl,bob=direct (the gw= option in the peers file wins). |
VPN_PEER_GATEWAY_FILE | (unset) | The map in a file; hot-reload with wg-reload. |
VPN_PEER_GATEWAY_WATCH | 0 | 1 = reload the map automatically on change. |
VPN_GATEWAY_TABLE / VPN_GATEWAY_RULE_PRIO / VPN_GATEWAY_PEER_RULE_PRIO / VPN_LOCAL_RULE_PRIO | 100 / 1000 / 900 / 800 | Routing table base and rule priorities (advanced). |
| Variable | Default | Description |
|---|---|---|
EXIT_SUBNET / EXIT_SUBNET6 | 10.10.11.0/24 / fda9:β¦:03eb::/64 | Transfer addresses for exit nodes. |
EXIT_PORT_BASE | LISTEN_PORT+1 | First UDP port handed to dial-in exit nodes (publish them). |
UPSTREAMS_DIR | /etc/wireguard/upstreams | Bring-your-own wg-quick-style confs (<name>.conf), each a gateway named <name>. |
UPSTREAM_HANDSHAKE_TIMEOUT | 180 | Seconds without a handshake before a DNS-named dial-out endpoint is re-resolved and re-set (always on). |
| Variable | Default | Description |
|---|---|---|
LOCAL_GATEWAY | direct | Exit for the container's own traffic and network_mode: service: containers; arms the output kill switch. |
FORWARD_FROM / FORWARD_FROM6 | (unset) | Source CIDRs (or auto) routed through LOCAL_GATEWAY. |
GATEWAY_NAT6 | true | NAT66 for sidecar clients. |
NETWORK / NETWORK6 | (unset) | LAN CIDRs the kill switch keeps reachable off-tunnel. |
DNS | 1.1.1.1,1.0.0.1 | Resolvers written to resolv.conf in sidecar mode. |
| Variable | Default | Description |
|---|---|---|
HEALTH_CHECK_ENABLED | false | true = the HEALTHCHECK probe checks wg0, the listen port and the nft tables. |
HEALTH_CHECK_EGRESS | (unset) | Paths that also gate health: direct, gateway names, default, upstreams, all (join with +). |
HEALTH_CHECK_URL | https://1.1.1.1/β¦ | URL(s) for the direct probe, ;-separated. |
| Command | Purpose |
|---|---|
peer-conf <name> [--qr] | print a peer's or exit node's device config (or QR) |
peer-conf --list | every defined node with address, exit and state |
peer-report [--json] | handshake, endpoint, traffic and gateway per peer |
wg-reload | apply the peers file and maps in place |
| `network-diagnostic [--basic | --json |
vpngw-gw-resolve | force a re-resolve of DNS-named gateways and endpoints |
--cap-add=NET_ADMIN and --sysctl net.ipv4.ip_forward=1 β interfaces, routes and nftables; forwarding peer traffic (detailsβ )wireguard module); without it the container falls back to userspace wireguard-go, which additionally needs --device /dev/net/tun and is slower (detailsβ )wg0 (51820 by default), plus one per dial-in exit node (detailsβ )--sysctl net.ipv6.conf.all.forwarding=1 and an IPv6-routed Docker network (detailsβ )If you have any problems with or questions about this image, please open a GitHub issueβ .
Check the Troubleshootingβ wiki page first β and attach the output of the built-in diagnostic to any report:
docker exec wireguard network-diagnostic
For how the container works under the hood, see Architecture and Internalsβ ; for building it yourself, image tags and releases, Building and CIβ .
MIT β see LICENSEβ .
Content type
Image
Digest
sha256:200cfa531β¦
Size
16.6 MB
Last updated
about 4 hours ago
docker pull azinchen/wireguard