Sign inSign up

azinchen/wireguard

By azinchen

β€’Updated about 4 hours ago

WireGuard VPN node in Docker: zero-config peers, per-peer exits (direct, upstream VPN containers, Wi

Image
0

310

azinchen/wireguard repository overview

⁠WireGuard VPN Docker Container

GitHub release GitHub release date GitHub build
GitHub stars GitHub forks Open issues GitHub last commit
Docker pulls Docker stars Docker image size
Multi-arch

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.

⁠✨ Key Features

  • 🧩 Zero-config peers β€” PEERS=alice,bob generates keys, addresses, configs and QR codes (details⁠)
  • πŸ”‘ Bring your own keys β€” a peer may give only its public key; no private key on the server (details⁠)
  • πŸšͺ Per-peer exits β€” direct, an upstream VPN container, a provider conf, or another peer (details⁠)
  • 🏠 Exit nodes β€” exit=true makes a peer such as a home router an exit; config generated (details⁠)
  • πŸ›‘οΈ Fail-closed kill switch β€” if an upstream is down, its peers lose internet, never leak (details⁠)
  • πŸ”„ Hot reload β€” wg-reload applies the peers file in place, unchanged peers keep sessions (details⁠)
  • 🧭 DNS-named gateways β€” sidecars and endpoints by hostname, re-resolved and re-routed live (details⁠)
  • πŸ”— Sidecar mode β€” LOCAL_GATEWAY tunnels the container and network_mode: service: clients (details⁠)
  • 🩺 Opt-in health monitor β€” HEALTHCHECK for the interface, routing tables and egress probes (details⁠)
  • πŸ“Š Built-in reporting β€” peer-report shows handshake, traffic and exit per peer (details⁠)
  • 🐧 Kernel WireGuard β€” uses the host module when present, falls back to wireguard-go (details⁠)
  • πŸ“¦ Small multi-arch Alpine image β€” 386, amd64, arm/v6, arm/v7, arm64, riscv64 (details⁠)

πŸ“– Full documentation on the Wiki⁠ β€” setup, peers, gateway mode, exit nodes, sidecar mode, configuration reference, troubleshooting, architecture.


β πŸš€ Quick Start

# 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

⁠🧩 Common Setups

I want to…Guide
Run a plain WireGuard server for my devicesGetting Started⁠ · Peers⁠
Send some peers out through NordVPN / another VPN containerGateway Mode⁠
Use a WireGuard provider (Mullvad, Proton, my VPS) as an exitExit 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 tunnelSidecar 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.

β βš™οΈ Environment Variables

Grouped by feature; every variable is one line here β€” the Configuration Reference⁠ has the full descriptions.

⁠Core networking
VariableDefaultDescription
VPN_SUBNET10.10.10.0/24Peer subnet; the server takes the first host, peers are allocated from the second.
LISTEN_PORT51820UDP 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.
MTU1420wg0 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_PEERtrueAllow peers to reach each other through wg0 (peer-to-peer traffic never enters a gateway).
⁠Peers β€” details⁠
VariableDefaultDescription
PEERS(unset)Inline list: alice,bob (keys generated) or bob=<pubkey> (device-made key).
PEERS_FILE/etc/wireguard/peers.confPeer definitions, one name [option=value ...] per line; file wins over PEERS.
PEERS_WATCH01 = wg-reload automatically whenever the peers file changes.
PEER_DNS1.1.1.1,1.0.0.1DNS = written into device configs (per-peer dns=).
PEER_ROUTES0.0.0.0/0,::/0Device-side AllowedIPs (full tunnel); per-peer routes= for split tunnels.
PEER_KEEPALIVE25PersistentKeepalive in device configs (0 = off).
⁠IPv6
VariableDefaultDescription
IPV6_FORWARD1Enable IPv6 forwarding inside the container.
IPV6_NAT0NAT66 for IPV6_SUBNET; peers only get an IPv6 address when IPv6 is actually routed.
IPV6_SUBNETfda9:…::/64ULA peer subnet (<prefix>::/64).
⁠Gateway mode β€” details⁠
VariableDefaultDescription
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_INTERVAL0Re-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_WATCH01 = reload the map automatically on change.
VPN_GATEWAY_TABLE / VPN_GATEWAY_RULE_PRIO / VPN_GATEWAY_PEER_RULE_PRIO / VPN_LOCAL_RULE_PRIO100 / 1000 / 900 / 800Routing table base and rule priorities (advanced).
⁠Exit nodes and upstreams β€” details⁠
VariableDefaultDescription
EXIT_SUBNET / EXIT_SUBNET610.10.11.0/24 / fda9:…:03eb::/64Transfer addresses for exit nodes.
EXIT_PORT_BASELISTEN_PORT+1First UDP port handed to dial-in exit nodes (publish them).
UPSTREAMS_DIR/etc/wireguard/upstreamsBring-your-own wg-quick-style confs (<name>.conf), each a gateway named <name>.
UPSTREAM_HANDSHAKE_TIMEOUT180Seconds without a handshake before a DNS-named dial-out endpoint is re-resolved and re-set (always on).
⁠Sidecar mode β€” details⁠
VariableDefaultDescription
LOCAL_GATEWAYdirectExit 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_NAT6trueNAT66 for sidecar clients.
NETWORK / NETWORK6(unset)LAN CIDRs the kill switch keeps reachable off-tunnel.
DNS1.1.1.1,1.0.0.1Resolvers written to resolv.conf in sidecar mode.
⁠Health monitor β€” details⁠
VariableDefaultDescription
HEALTH_CHECK_ENABLEDfalsetrue = 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_URLhttps://1.1.1.1/…URL(s) for the direct probe, ;-separated.

β πŸ› οΈ Operator Commands

CommandPurpose
peer-conf <name> [--qr]print a peer's or exit node's device config (or QR)
peer-conf --listevery defined node with address, exit and state
peer-report [--json]handshake, endpoint, traffic and gateway per peer
wg-reloadapply the peers file and maps in place
`network-diagnostic [--basic--json
vpngw-gw-resolveforce a re-resolve of DNS-named gateways and endpoints

β πŸ“‹ Runtime Requirements

  • --cap-add=NET_ADMIN and --sysctl net.ipv4.ip_forward=1 β€” interfaces, routes and nftables; forwarding peer traffic (details⁠)
  • a kernel with WireGuard support (5.6+ built in, or the wireguard module); without it the container falls back to userspace wireguard-go, which additionally needs --device /dev/net/tun and is slower (details⁠)
  • a published UDP port for wg0 (51820 by default), plus one per dial-in exit node (details⁠)
  • for IPv6 peers: --sysctl net.ipv6.conf.all.forwarding=1 and an IPv6-routed Docker network (details⁠)

β πŸ› Issues

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⁠.

β πŸ“„ License

MIT β€” see LICENSE⁠.

Tag summary

Content type

Image

Digest

sha256:200cfa531…

Size

16.6 MB

Last updated

about 4 hours ago

docker pull azinchen/wireguard