WireGuard Site-to-Site VPN Configuration
WireGuard is a modern, minimalist VPN protocol that offers better performance and security than traditional solutions like IPsec and OpenVPN. With just under 4,000 lines of code, WireGuard provides cryptographically proven security through ChaCha20 for encryption and Poly1305 for authentication. This guide covers generating cryptographic keys, configuring peer relationships, setting up routing between sites, managing firewall rules, implementing persistent keepalive, and deploying multi-site mesh networks.
Table of Contents
- System Requirements
- Installation
- Key Generation
- Basic Peer Configuration
- Site-to-Site Setup
- Routing Configuration
- iptables Integration
- Persistent Keepalive
- Multi-Site Mesh Networks
- Performance Tuning
- Troubleshooting
- Conclusion
System Requirements
WireGuard runs on most modern Linux distributions with kernel 5.6 or later. Verify your system:
uname -r
cat /etc/os-release
nproc
free -h
For kernel versions before 5.6, WireGuard can run in userspace mode but with reduced performance.
Installation
Install WireGuard from official repositories. The installation includes both kernel module (built-in on newer kernels) and userspace tools.
For Ubuntu/Debian:
sudo apt-get update
sudo apt-get install -y wireguard wireguard-tools
For CentOS/RHEL 8+:
sudo dnf install -y wireguard-tools
For Fedora:
sudo dnf install -y wireguard-tools
For older systems without in-kernel support:
sudo apt-get install -y wireguard-dkms
Verify installation:
wg --version
ip link show type wireguard
Enable IP forwarding for routing between sites:
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv6.conf.all.forwarding=1
Make IP forwarding persistent:
sudo nano /etc/sysctl.conf
Add or uncomment:
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
Apply changes:
sudo sysctl -p
Key Generation
WireGuard uses elliptic curve cryptography with Curve25519 keys. Generate keys for each peer.
Generate a private key:
wg genkey > private.key
cat private.key
Generate the corresponding public key:
wg pubkey < private.key > public.key
cat public.key
Generate a pre-shared key for additional security (optional):
wg genpsk > preshared.key
cat preshared.key
Secure key files:
chmod 600 private.key preshared.key
For multi-site deployments, generate unique key pairs for each site:
# Site A
mkdir -p /etc/wireguard/keys/site-a
cd /etc/wireguard/keys/site-a
wg genkey | tee private.key | wg pubkey > public.key
chmod 600 private.key
# Site B
mkdir -p /etc/wireguard/keys/site-b
cd /etc/wireguard/keys/site-b
wg genkey | tee private.key | wg pubkey > public.key
chmod 600 private.key
Basic Peer Configuration
Create a WireGuard interface with peer configuration. WireGuard interfaces are configured as regular network interfaces.
Create the WireGuard configuration file:
sudo nano /etc/wireguard/wg0.conf
Basic configuration for Site A (acting as server):
[Interface]
# Interface name
Address = 10.0.0.1/24
# Private key for this site
PrivateKey = <site-a-private-key>
# Listen port for incoming connections
ListenPort = 51820
# Optional: DNS servers
PostUp = resolvectl default-route wg0 yes
PostDown = resolvectl default-route wg0 no
[Peer]
# Public key of Site B
PublicKey = <site-b-public-key>
# Allowed IPs from this peer
AllowedIPs = 10.0.0.2/32
# Optional: Pre-shared key for additional security
PresharedKey = <optional-preshared-key>
Set restrictive permissions on the configuration file:
sudo chmod 600 /etc/wireguard/wg0.conf
sudo chown root:root /etc/wireguard/wg0.conf
Enable and bring up the interface:
sudo wg-quick up wg0
Verify the interface is active:
sudo ip addr show wg0
sudo ip link show wg0
sudo wg show
Check WireGuard status:
sudo wg show wg0
Output shows:
interface: wg0
public key: <public-key>
private key: (hidden)
listening port: 51820
peer: <peer-public-key>
allowed ips: 10.0.0.2/32
latest handshake: (never)
transfer: 0 B received, 0 B sent
persistent keepalive: off
Site-to-Site Setup
Configure two sites with WireGuard for secure site-to-site connectivity.
Site A configuration (203.0.113.1, local network 192.168.1.0/24):
sudo nano /etc/wireguard/wg0.conf
Content:
[Interface]
Address = 10.0.0.1/24
PrivateKey = <site-a-private-key>
ListenPort = 51820
[Peer]
PublicKey = <site-b-public-key>
AllowedIPs = 10.0.0.2/32, 192.168.2.0/24
Endpoint = 198.51.100.5:51820
PersistentKeepalive = 25
Site B configuration (198.51.100.5, local network 192.168.2.0/24):
sudo nano /etc/wireguard/wg0.conf
Content:
[Interface]
Address = 10.0.0.2/24
PrivateKey = <site-b-private-key>
ListenPort = 51820
[Peer]
PublicKey = <site-a-public-key>
AllowedIPs = 10.0.0.1/32, 192.168.1.0/24
Endpoint = 203.0.113.1:51820
PersistentKeepalive = 25
Bring up WireGuard on both sites:
sudo wg-quick up wg0
Test connectivity:
ping -c 4 10.0.0.2 # From Site A to Site B VPN IP
ping -c 4 192.168.2.5 # From Site A to Site B local network
Verify peer connection:
sudo wg show
Should show active handshake and data transfer.
Routing Configuration
Configure routing to enable traffic between local networks at each site.
Site A: Route traffic destined for Site B's local network through WireGuard:
sudo ip route add 192.168.2.0/24 via 10.0.0.2 dev wg0
Site B: Route traffic destined for Site A's local network through WireGuard:
sudo ip route add 192.168.1.0/24 via 10.0.0.1 dev wg0
Make routing persistent with Netplan (Ubuntu/Debian):
sudo nano /etc/netplan/99-wireguard.yaml
Content:
network:
version: 2
renderer: networkd
tunnels:
wg0:
mode: wireguard
mtu: 1420
private-key: '<site-private-key>'
addresses:
- 10.0.0.1/24
peers:
- public-key: '<peer-public-key>'
allowed-ips:
- 10.0.0.2/32
- 192.168.2.0/24
endpoint: 198.51.100.5:51820
persistent-keepalive: 25
routes:
- to: 192.168.2.0/24
via: 10.0.0.2
metric: 100
Apply Netplan configuration:
sudo netplan apply
Verify routes:
ip route show
For NetworkManager (alternative):
sudo nano /etc/NetworkManager/conf.d/99-wireguard.conf
Add:
[main]
dhcp=auto
iptables Integration
Configure firewall rules to allow WireGuard traffic and route site traffic properly.
Allow WireGuard port through firewall:
sudo iptables -A INPUT -p udp --dport 51820 -j ACCEPT
Allow traffic between WireGuard peers:
sudo iptables -A FORWARD -i wg0 -j ACCEPT
sudo iptables -A FORWARD -o wg0 -j ACCEPT
Configure NAT to masquerade traffic from WireGuard interface:
sudo iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
Save iptables rules:
sudo iptables-save > /etc/iptables/rules.v4
Configure ufw (simplified firewall):
sudo ufw allow 51820/udp
sudo ufw allow in on wg0
Alternatively, use nftables for modern systems:
sudo nano /etc/nftables.conf
Add rules:
table inet wireguard {
chain input {
type filter hook input priority 0;
# Allow WireGuard port
udp dport 51820 accept
}
chain forward {
type filter hook forward priority 0;
# Allow WireGuard traffic
iifname "wg0" accept
oifname "wg0" accept
}
}
Apply nftables:
sudo nft -f /etc/nftables.conf
Persistent Keepalive
Configure persistent keepalive to maintain VPN connections through NAT and firewalls.
Add PersistentKeepalive to WireGuard configuration:
sudo nano /etc/wireguard/wg0.conf
Modify peer section:
[Peer]
PublicKey = <peer-public-key>
AllowedIPs = 10.0.0.2/32, 192.168.2.0/24
Endpoint = 198.51.100.5:51820
PersistentKeepalive = 25
The value (25) is in seconds. Lower values increase overhead but improve responsiveness. Typical values:
- 0 = disabled (for endpoint initiators)
- 10-30 = NAT traversal (most common)
- 60+ = low-overhead keepalive
Apply changes:
sudo wg-quick down wg0
sudo wg-quick up wg0
Verify keepalive is active:
sudo wg show
Should show "persistent keepalive" interval.
Multi-Site Mesh Networks
Configure multiple sites to communicate directly in a mesh topology.
Create a mesh with 3 sites (A, B, C):
Site A (10.0.0.1/24):
[Interface]
Address = 10.0.0.1/24
PrivateKey = <site-a-private-key>
ListenPort = 51820
[Peer]
# Site B
PublicKey = <site-b-public-key>
AllowedIPs = 10.0.0.2/32, 192.168.2.0/24
Endpoint = 198.51.100.5:51820
PersistentKeepalive = 25
[Peer]
# Site C
PublicKey = <site-c-public-key>
AllowedIPs = 10.0.0.3/32, 192.168.3.0/24
Endpoint = 203.0.113.10:51820
PersistentKeepalive = 25
Site B (10.0.0.2/24):
[Interface]
Address = 10.0.0.2/24
PrivateKey = <site-b-private-key>
ListenPort = 51820
[Peer]
# Site A
PublicKey = <site-a-public-key>
AllowedIPs = 10.0.0.1/32, 192.168.1.0/24
Endpoint = 203.0.113.1:51820
PersistentKeepalive = 25
[Peer]
# Site C
PublicKey = <site-c-public-key>
AllowedIPs = 10.0.0.3/32, 192.168.3.0/24
Endpoint = 203.0.113.10:51820
PersistentKeepalive = 25
Site C (10.0.0.3/24):
[Interface]
Address = 10.0.0.3/24
PrivateKey = <site-c-private-key>
ListenPort = 51820
[Peer]
# Site A
PublicKey = <site-a-public-key>
AllowedIPs = 10.0.0.1/32, 192.168.1.0/24
Endpoint = 203.0.113.1:51820
PersistentKeepalive = 25
[Peer]
# Site B
PublicKey = <site-b-public-key>
AllowedIPs = 10.0.0.2/32, 192.168.2.0/24
Endpoint = 198.51.100.5:51820
PersistentKeepalive = 25
Bring up all sites:
sudo wg-quick up wg0
Test mesh connectivity:
# From Site A
ping 10.0.0.2 # Site B VPN IP
ping 10.0.0.3 # Site C VPN IP
ping 192.168.2.5 # Site B local network
ping 192.168.3.10 # Site C local network
Verify mesh topology:
sudo wg show
# Should show all peers with active handshakes
Performance Tuning
Optimize WireGuard performance for high-bandwidth scenarios.
Configure MTU (Maximum Transmission Unit):
sudo ip link set dev wg0 mtu 1420
Or in Netplan:
tunnels:
wg0:
mtu: 1420
Enable UDP segmentation offload:
sudo ethtool -K eth0 tx-udp_tnl-segmentation on
Monitor performance:
sudo wg show
Shows real-time packet statistics and transfer rates.
Use iperf to benchmark throughput:
# Site B (server)
iperf3 -s
# Site A (client)
iperf3 -c 10.0.0.2
Adjust ring buffer sizes for network interface:
sudo ethtool -G eth0 rx 4096 tx 4096
Troubleshooting
Diagnose common WireGuard issues.
Enable debug logging:
sudo modprobe wireguard
sudo echo "module wireguard +p" > /sys/kernel/debug/dynamic_debug/control
journalctl -u systemd-modules-load -xe
Verify interface is up:
ip link show dev wg0
ip addr show dev wg0
Test connectivity through WireGuard:
ping -c 4 10.0.0.2
traceroute 10.0.0.2
mtr 10.0.0.2
Check open ports:
sudo ss -ulnp | grep wireguard
sudo netstat -ulnp | grep 51820
View WireGuard status details:
sudo wg show all
sudo wg show wg0 peers
sudo wg show wg0 allowed-ips
Verify pre-shared key if configured:
sudo cat /etc/wireguard/wg0.conf | grep -i preshared
Check firewall rules:
sudo iptables -L INPUT -n | grep 51820
sudo iptables -L FORWARD -n | grep wg
sudo ufw status
Conclusion
WireGuard provides a modern, efficient VPN solution for site-to-site connectivity with minimal overhead and strong cryptographic security. By following this guide, you've generated cryptographic keys, configured peer relationships, set up direct site-to-site tunnels, implemented routing for transparent access to remote networks, integrated firewall rules, deployed persistent keepalive for NAT traversal, and built mesh networks with multiple sites. WireGuard's simplicity and performance make it ideal for connecting infrastructure, data centers, and branch offices. Regular monitoring and security updates ensure continued reliable operation of your site-to-site network infrastructure.


