WireGuard Cheat Sheet
Overview
WireGuard is a fast, modern VPN protocol and implementation designed to be simpler, leaner, and more useful than IPsec and OpenVPN. It uses cutting-edge cryptography including Curve25519 for key exchange, ChaCha20 for encryption, Poly1305 for authentication, BLAKE2s for hashing, and SipHash for hashtable keys. The entire codebase is approximately 4,000 lines — an order of magnitude smaller than IPsec or OpenVPN — making it far easier to audit.
WireGuard operates at Layer 3, creating a virtual network interface (wg0, wg1, etc.) that behaves like any other network interface but encrypts all traffic. It is built into the Linux kernel from version 5.6 onward, and kernel-native performance makes it significantly faster than userspace VPN solutions. WireGuard uses a concept called “cryptokey routing” — each peer is identified solely by its public key, and the allowed IP ranges for that peer determine how packets are routed.
The protocol is stateless by design: there is no handshake phase visible to network observers, no connection state, and endpoints roam freely. If a client changes IP addresses, the tunnel continues working transparently. This makes WireGuard ideal for mobile devices that frequently switch between networks. WireGuard also implements a “silent” mode when no data is flowing — it sends no keepalive packets and leaves no detectable fingerprint on the network when idle.
Installation
Ubuntu/Debian
# Ubuntu 20.04+ and Debian 11+ include WireGuard
sudo apt update && sudo apt install -y wireguard wireguard-tools
# Older Ubuntu via backports
sudo add-apt-repository ppa:wireguard/wireguard
sudo apt update && sudo apt install -y wireguard
# Verify kernel module
modprobe wireguard
lsmod | grep wireguard
RHEL/CentOS/Fedora
# Fedora
sudo dnf install -y wireguard-tools
# RHEL 8/9 with EPEL
sudo dnf install -y epel-release
sudo dnf install -y wireguard-tools kmod-wireguard
# Enable kernel module
sudo modprobe wireguard
echo 'wireguard' | sudo tee /etc/modules-load.d/wireguard.conf
macOS
# Install WireGuard tools via Homebrew
brew install wireguard-tools
# Or use the official macOS app from the App Store
# App Store: "WireGuard"
# Use wg-quick with macOS
sudo wg-quick up /etc/wireguard/wg0.conf
Windows
# Download the official installer from https://www.wireguard.com/install/
# Or via winget
winget install WireGuard.WireGuard
# Or via Chocolatey
choco install wireguard
Docker
# Run WireGuard in Docker (linuxserver image)
docker run -d \
--name=wireguard \
--cap-add=NET_ADMIN \
--cap-add=SYS_MODULE \
-e PUID=1000 -e PGID=1000 \
-e SERVERURL=vpn.example.com \
-e PEERS=phone,laptop \
-p 51820:51820/udp \
-v /opt/wireguard:/config \
-v /lib/modules:/lib/modules:ro \
--sysctl="net.ipv4.conf.all.src_valid_mark=1" \
--restart unless-stopped \
lscr.io/linuxserver/wireguard:latest
Configuration
Key Generation
# Generate private/public key pair
wg genkey | tee privatekey | wg pubkey > publickey
# Generate preshared key (optional, quantum-resistant layer)
wg genpsk > presharedkey
# View keys
cat privatekey
cat publickey
# Generate keys inline (for config)
PRIVATE=$(wg genkey)
PUBLIC=$(echo $PRIVATE | wg pubkey)
echo "Private: $PRIVATE"
echo "Public: $PUBLIC"
Server Configuration (/etc/wireguard/wg0.conf)
[Interface]
# Server's private key
PrivateKey = <SERVER_PRIVATE_KEY>
# VPN subnet address for this server
Address = 10.0.0.1/24
# UDP port to listen on
ListenPort = 51820
# Enable NAT for internet access through VPN
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# DNS server for peers (optional)
DNS = 1.1.1.1
[Peer]
# Client 1 - Laptop
PublicKey = <CLIENT1_PUBLIC_KEY>
PresharedKey = <PRESHARED_KEY>
AllowedIPs = 10.0.0.2/32
[Peer]
# Client 2 - Phone
PublicKey = <CLIENT2_PUBLIC_KEY>
AllowedIPs = 10.0.0.3/32
Client Configuration (/etc/wireguard/wg0.conf)
[Interface]
# Client's private key
PrivateKey = <CLIENT_PRIVATE_KEY>
# Client's VPN IP address
Address = 10.0.0.2/24
# Optional: set DNS when tunnel is up
DNS = 10.0.0.1
[Peer]
# Server's public key
PublicKey = <SERVER_PUBLIC_KEY>
PresharedKey = <PRESHARED_KEY>
# Server's public endpoint
Endpoint = vpn.example.com:51820
# Route all traffic through VPN (full tunnel)
AllowedIPs = 0.0.0.0/0, ::/0
# Send keepalive to maintain NAT mapping
PersistentKeepalive = 25
Enable IP Forwarding (Server)
# Enable immediately
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv6.conf.all.forwarding=1
# Make persistent
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
echo "net.ipv6.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf
Core Commands
| Command | Description |
|---|---|
wg genkey | Generate a private key |
wg pubkey < privatekey | Derive public key from private key |
wg genpsk | Generate a preshared key |
wg show | Show current WireGuard interface status |
wg show wg0 | Show status for specific interface |
wg showconf wg0 | Show full current config for interface |
wg set wg0 peer PUB_KEY allowed-ips IP | Add/update a peer dynamically |
wg set wg0 peer PUB_KEY remove | Remove a peer dynamically |
wg-quick up wg0 | Bring up interface using config file |
wg-quick down wg0 | Bring down interface |
wg-quick strip wg0 | Show config stripped of wg-quick fields |
ip link show wg0 | Show WireGuard network interface |
ip addr show wg0 | Show interface addresses |
ip route show table main | Show routing table |
Advanced Usage
Split Tunneling (Route Only Specific Traffic)
# Client config — only route specific subnets through VPN
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = vpn.example.com:51820
# Only route internal subnets through VPN, not all traffic
AllowedIPs = 10.0.0.0/8, 192.168.1.0/24, 172.16.0.0/12
PersistentKeepalive = 25
Kill Switch Configuration
[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 10.0.0.2/24
DNS = 10.0.0.1
# Drop all traffic if VPN goes down
PostUp = iptables -I OUTPUT ! -o wg0 -m mark ! --mark $(wg show wg0 fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PreDown = iptables -D OUTPUT ! -o wg0 -m mark ! --mark $(wg show wg0 fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
Site-to-Site VPN
# Site A server — 192.168.1.0/24 local subnet
[Interface]
PrivateKey = <SITE_A_PRIVATE_KEY>
Address = 10.0.0.1/30
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
[Peer]
# Site B
PublicKey = <SITE_B_PUBLIC_KEY>
Endpoint = site-b.example.com:51820
# Allow Site B's VPN IP and local subnet
AllowedIPs = 10.0.0.2/32, 192.168.2.0/24
PersistentKeepalive = 25
# Site B server — 192.168.2.0/24 local subnet
[Interface]
PrivateKey = <SITE_B_PRIVATE_KEY>
Address = 10.0.0.2/30
ListenPort = 51820
[Peer]
# Site A
PublicKey = <SITE_A_PUBLIC_KEY>
Endpoint = site-a.example.com:51820
AllowedIPs = 10.0.0.1/32, 192.168.1.0/24
PersistentKeepalive = 25
IPv6 Dual-Stack
[Interface]
PrivateKey = <PRIVATE_KEY>
Address = 10.0.0.1/24, fd00::1/64
ListenPort = 51820
[Peer]
PublicKey = <PEER_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32, fd00::2/128
Systemd Integration
# Enable WireGuard interface on boot
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo systemctl status wg-quick@wg0
# Restart after config change
sudo systemctl restart wg-quick@wg0
# View logs
journalctl -u wg-quick@wg0 -f
NAT Traversal and Dynamic Endpoints
# Add peer with dynamic endpoint (updates on handshake)
sudo wg set wg0 peer <PUBLIC_KEY> \
endpoint vpn.example.com:51820 \
allowed-ips 10.0.0.2/32 \
persistent-keepalive 25
# Save running config back to file
sudo wg showconf wg0 > /etc/wireguard/wg0.conf
# Dynamic peer management script
#!/bin/bash
# Add peer
wg set wg0 peer "$1" allowed-ips "$2" persistent-keepalive 25
wg-quick save wg0
Common Workflows
Quick Server Setup
# 1. Install WireGuard
sudo apt install -y wireguard-tools
# 2. Generate keys
cd /etc/wireguard
umask 077
wg genkey | tee server_private.key | wg pubkey > server_public.key
# 3. Create server config
cat > /etc/wireguard/wg0.conf << EOF
[Interface]
PrivateKey = $(cat server_private.key)
Address = 10.0.0.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o $(ip route | awk '/default/ {print $5}') -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o $(ip route | awk '/default/ {print $5}') -j MASQUERADE
EOF
# 4. Enable IP forwarding
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.d/99-wireguard.conf
sysctl -p /etc/sysctl.d/99-wireguard.conf
# 5. Start and enable
sudo systemctl enable --now wg-quick@wg0
# 6. Open firewall
sudo ufw allow 51820/udp
Adding a New Client
# On server — generate client keys
CLIENT_PRIV=$(wg genkey)
CLIENT_PUB=$(echo $CLIENT_PRIV | wg pubkey)
CLIENT_IP="10.0.0.5"
# Add peer to server (live, no restart)
sudo wg set wg0 peer $CLIENT_PUB allowed-ips ${CLIENT_IP}/32
# Save to config file
sudo wg-quick save wg0
# Create client config (send securely to client)
cat << EOF
[Interface]
PrivateKey = $CLIENT_PRIV
Address = ${CLIENT_IP}/24
DNS = 10.0.0.1
[Peer]
PublicKey = $(cat /etc/wireguard/server_public.key)
Endpoint = $(curl -s ifconfig.me):51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
EOF
Generating QR Codes for Mobile Clients
# Install qrencode
sudo apt install -y qrencode
# Display client config as QR code in terminal
qrencode -t ansiutf8 < /etc/wireguard/client.conf
# Save as PNG for sharing
qrencode -o client-qr.png < /etc/wireguard/client.conf
Monitoring and Diagnostics
# Show all peers and their status
watch -n 1 wg show
# Check handshake timestamps (non-zero = connected)
wg show wg0 latest-handshakes
# Monitor traffic
wg show wg0 transfer
# Capture WireGuard traffic (UDP port 51820)
sudo tcpdump -i eth0 -n 'udp port 51820'
# Check routing
ip route show table main
ip route show table 51820 # WireGuard's routing table
# Test connectivity through tunnel
ping 10.0.0.1 # Ping server VPN IP
traceroute 8.8.8.8 # Verify traffic routing
Tips and Best Practices
| Practice | Details |
|---|---|
| Protect private keys | Set umask 077 before generating; store only in /etc/wireguard/ owned by root |
| Use preshared keys | Adds a symmetric key layer for post-quantum resistance |
| Restrict AllowedIPs | Use specific subnets instead of 0.0.0.0/0 when split tunneling |
| PersistentKeepalive 25 | Use only on clients behind NAT; servers should not set this |
| DNS leaks | Set DNS in [Interface] and verify with a DNS leak test |
| Rotate keys regularly | Regenerate peer keys periodically to limit exposure from compromise |
| Audit peer list | Run wg show regularly to verify all peers are expected |
| Use port 443 | Some networks block 51820; WireGuard works on any UDP port |
| Firewall first | Configure firewall before starting the tunnel on a server |
| Log monitoring | Use journalctl -u wg-quick@wg0 and set up alerting on handshake failures |