Introduction
WireGuard’s simplicity makes it adaptable to many scenarios. This guide shows concrete configurations for the most common use cases โ not theory, but working configs you can adapt.
Use Case 1: Remote Access VPN
The classic use case: employees connecting to the office network from home.
Architecture
Employee laptop (10.0.0.2) โโWireGuardโโโ VPN Server (10.0.0.1) โโโ Office Network (192.168.1.0/24)
Server Config
# /etc/wireguard/wg0.conf โ VPN server
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <server_private_key>
# Route traffic to office network
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -A FORWARD -o wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -o wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# Alice's laptop
PublicKey = <alice_public_key>
AllowedIPs = 10.0.0.2/32
[Peer]
# Bob's laptop
PublicKey = <bob_public_key>
AllowedIPs = 10.0.0.3/32
Client Config (Split Tunnel โ only office traffic through VPN)
# Alice's laptop โ only routes office network through VPN
[Interface]
Address = 10.0.0.2/24
PrivateKey = <alice_private_key>
DNS = 192.168.1.1 # office DNS server
[Peer]
PublicKey = <server_public_key>
Endpoint = vpn.company.com:51820
# Only route office network through VPN (split tunnel)
AllowedIPs = 10.0.0.0/24, 192.168.1.0/24
PersistentKeepalive = 25
Client Config (Full Tunnel โ all traffic through VPN)
[Interface]
Address = 10.0.0.2/24
PrivateKey = <alice_private_key>
DNS = 1.1.1.1
[Peer]
PublicKey = <server_public_key>
Endpoint = vpn.company.com:51820
# Route ALL traffic through VPN
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
Use Case 2: Site-to-Site VPN
Connect two office networks so they can communicate directly.
Architecture
Office A (192.168.1.0/24) โโWireGuardโโโ Office B (192.168.2.0/24)
VPN IP: 10.0.0.1 VPN IP: 10.0.0.2
Office A Config
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <office_a_private_key>
# Route Office B's subnet through the tunnel
PostUp = ip route add 192.168.2.0/24 via 10.0.0.2 dev wg0
PostDown = ip route del 192.168.2.0/24
[Peer]
PublicKey = <office_b_public_key>
Endpoint = office-b.example.com:51820
# Office B's VPN IP + Office B's LAN
AllowedIPs = 10.0.0.2/32, 192.168.2.0/24
PersistentKeepalive = 25
Office B Config
[Interface]
Address = 10.0.0.2/24
ListenPort = 51820
PrivateKey = <office_b_private_key>
PostUp = ip route add 192.168.1.0/24 via 10.0.0.1 dev wg0
PostDown = ip route del 192.168.1.0/24
[Peer]
PublicKey = <office_a_public_key>
Endpoint = office-a.example.com:51820
AllowedIPs = 10.0.0.1/32, 192.168.1.0/24
PersistentKeepalive = 25
Test Connectivity
# From Office A, ping a machine in Office B
ping 192.168.2.100
# Check routing
ip route show | grep 192.168.2
# 192.168.2.0/24 via 10.0.0.2 dev wg0
Use Case 3: Hub-and-Spoke (Multiple Offices)
One central hub, multiple spoke offices. Spokes communicate through the hub.
Spoke A (10.0.0.2) โโโ Hub (10.0.0.1) โโโ Spoke B (10.0.0.3)
โ
Spoke C (10.0.0.4)
Hub Config
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <hub_private_key>
# Enable routing between spokes
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -A FORWARD -o wg0 -j ACCEPT
[Peer]
# Spoke A
PublicKey = <spoke_a_public_key>
AllowedIPs = 10.0.0.2/32, 192.168.10.0/24 # Spoke A's LAN
[Peer]
# Spoke B
PublicKey = <spoke_b_public_key>
AllowedIPs = 10.0.0.3/32, 192.168.20.0/24 # Spoke B's LAN
[Peer]
# Spoke C
PublicKey = <spoke_c_public_key>
AllowedIPs = 10.0.0.4/32, 192.168.30.0/24 # Spoke C's LAN
Spoke Config (same pattern for all spokes)
[Interface]
Address = 10.0.0.2/24
PrivateKey = <spoke_a_private_key>
[Peer]
PublicKey = <hub_public_key>
Endpoint = hub.example.com:51820
# Route all other spokes' traffic through hub
AllowedIPs = 10.0.0.0/24, 192.168.20.0/24, 192.168.30.0/24
PersistentKeepalive = 25
Use Case 4: Kubernetes Pod Networking
WireGuard can encrypt pod-to-pod traffic across nodes (used by Calico, Cilium, Flannel):
# Cilium with WireGuard encryption
helm install cilium cilium/cilium \
--set encryption.enabled=true \
--set encryption.type=wireguard \
--set encryption.nodeEncryption=true
# Verify WireGuard is active
kubectl exec -n kube-system cilium-xxx -- cilium status | grep Encryption
# Encryption: WireGuard (enabled)
# Check WireGuard interfaces on nodes
kubectl debug node/worker-1 -it --image=busybox -- wg show
Manual Kubernetes Node-to-Node Encryption
# On each Kubernetes node, create a WireGuard interface
# Node 1 (10.0.0.1) โโ Node 2 (10.0.0.2)
# Node 1
wg genkey | tee /etc/wireguard/k8s.key | wg pubkey > /etc/wireguard/k8s.pub
cat > /etc/wireguard/k8s.conf << EOF
[Interface]
Address = 10.100.0.1/24
PrivateKey = $(cat /etc/wireguard/k8s.key)
ListenPort = 51821
[Peer]
PublicKey = <node2_public_key>
Endpoint = 192.168.1.2:51821 # Node 2's real IP
AllowedIPs = 10.100.0.2/32, 10.244.2.0/24 # Node 2's pod CIDR
EOF
wg-quick up k8s
Use Case 5: IoT Device Management
Securely manage thousands of IoT devices without exposing them to the internet.
Architecture
IoT Device (Raspberry Pi) โโWireGuardโโโ Management Server
โ
SSH, monitoring, updates
IoT Device Config (minimal)
# /etc/wireguard/mgmt.conf โ on each IoT device
[Interface]
Address = 10.10.0.{DEVICE_ID}/24
PrivateKey = <device_private_key>
[Peer]
PublicKey = <server_public_key>
Endpoint = mgmt.example.com:51820
# Only route management server traffic through VPN
AllowedIPs = 10.10.0.1/32
PersistentKeepalive = 30 # keep alive through NAT
Automated Device Provisioning
#!/bin/bash
# provision-device.sh โ run during device manufacturing/setup
DEVICE_ID=$1
SERVER_PUBLIC_KEY=$(cat /etc/wireguard/server_public.key)
SERVER_ENDPOINT="mgmt.example.com:51820"
VPN_IP="10.10.0.${DEVICE_ID}"
# Generate device keys
DEVICE_PRIVATE=$(wg genkey)
DEVICE_PUBLIC=$(echo "$DEVICE_PRIVATE" | wg pubkey)
# Register device on server
ssh mgmt-server "sudo wg set wg0 peer $DEVICE_PUBLIC allowed-ips ${VPN_IP}/32"
ssh mgmt-server "echo '[Peer]
PublicKey = $DEVICE_PUBLIC
AllowedIPs = ${VPN_IP}/32
# Device ID: $DEVICE_ID' | sudo tee -a /etc/wireguard/wg0.conf"
# Write config to device
cat > /etc/wireguard/mgmt.conf << EOF
[Interface]
Address = ${VPN_IP}/24
PrivateKey = $DEVICE_PRIVATE
[Peer]
PublicKey = $SERVER_PUBLIC_KEY
Endpoint = $SERVER_ENDPOINT
AllowedIPs = 10.10.0.1/32
PersistentKeepalive = 30
EOF
systemctl enable --now wg-quick@mgmt
echo "Device $DEVICE_ID provisioned with VPN IP $VPN_IP"
Use Case 6: Zero-Trust Access with wg-easy
wg-easy provides a web UI for managing WireGuard peers:
# docker-compose.yml
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy
container_name: wg-easy
environment:
- WG_HOST=vpn.example.com
- PASSWORD_HASH=$$2y$$10$$... # bcrypt hash of admin password
- WG_DEFAULT_DNS=1.1.1.1
- WG_DEFAULT_ADDRESS=10.8.0.x
- WG_ALLOWED_IPS=10.8.0.0/24, 192.168.1.0/24
volumes:
- ./wg-easy:/etc/wireguard
ports:
- "51820:51820/udp"
- "51821:51821/tcp" # web UI
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
restart: unless-stopped
docker compose up -d
# Web UI: http://your-server:51821
# Create clients, download configs, generate QR codes
Use Case 7: Bypass Restrictive Firewalls
Some networks block UDP. Use WireGuard over TCP with udp2raw:
# Server: wrap WireGuard UDP in TCP
udp2raw -s -l 0.0.0.0:443 -r 127.0.0.1:51820 -k "password" --raw-mode faketcp
# Client: unwrap TCP back to UDP
udp2raw -c -l 127.0.0.1:51820 -r server.example.com:443 -k "password" --raw-mode faketcp
# Client WireGuard config: point to local udp2raw
[Peer]
Endpoint = 127.0.0.1:51820 # local udp2raw port
Monitoring All Use Cases
# Universal monitoring commands
sudo wg show # all interfaces
sudo wg show wg0 transfer # bytes sent/received per peer
sudo wg show wg0 latest-handshakes # last handshake time per peer
# Alert if peer hasn't handshaked in 3 minutes (likely disconnected)
sudo wg show wg0 latest-handshakes | while read peer timestamp; do
age=$(($(date +%s) - timestamp))
if [ $age -gt 180 ]; then
echo "WARNING: Peer $peer last handshake ${age}s ago"
fi
done
# Prometheus metrics via wireguard_exporter
docker run -d -p 9586:9586 \
-v /etc/wireguard:/etc/wireguard:ro \
--cap-add NET_ADMIN \
mindflavor/prometheus-wireguard-exporter
Comments