Configure WireGuard site-to-site VPN connections with advanced routing and security

Intermediate 45 min Apr 18, 2026 669 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up secure network-to-network VPN tunnels using WireGuard with advanced routing, firewall rules, and monitoring for connecting multiple office locations or data centers.

Prerequisites

  • Two Linux servers with public IP addresses
  • Root or sudo access on both servers
  • Basic understanding of networking and IP routing
  • Firewall configuration knowledge

What this solves

WireGuard site-to-site VPN connections enable secure communication between entire networks rather than individual clients. This setup allows offices, data centers, or cloud environments to connect their internal networks securely over the internet, creating a unified private network infrastructure.

WireGuard site-to-site architecture and planning

Site-to-site VPN requires careful network planning to avoid IP conflicts and ensure proper routing. Each site needs a unique subnet, and both sites must route traffic destined for the remote network through their respective WireGuard interfaces.

Network planning example: Site A uses 192.168.1.0/24, Site B uses 192.168.2.0/24. The WireGuard tunnel uses 10.100.0.0/30 with Site A as 10.100.0.1 and Site B as 10.100.0.2.

This tutorial demonstrates connecting two sites with full network access between their internal subnets. Each site will act as a gateway for its local network, routing traffic through the WireGuard tunnel.

Step-by-step configuration

Install WireGuard on both sites

Install WireGuard and related networking tools on the gateway servers at both locations.

sudo apt update
sudo apt install -y wireguard wireguard-tools iptables-persistent
sudo dnf update -y
sudo dnf install -y wireguard-tools iptables-services
sudo systemctl enable --now iptables

Generate key pairs for both sites

Create public and private key pairs for each site. Run these commands on each gateway server to generate its own keys.

sudo mkdir -p /etc/wireguard
sudo wg genkey | sudo tee /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key
sudo chmod 600 /etc/wireguard/private.key
sudo chmod 644 /etc/wireguard/public.key

Display the keys for configuration exchange between sites:

echo "Private key:" && sudo cat /etc/wireguard/private.key
echo "Public key:" && cat /etc/wireguard/public.key

Configure primary site WireGuard server

Configure the primary site (Site A) with network 192.168.1.0/24. This server will listen for connections from the secondary site.

[Interface]
PrivateKey = SITE_A_PRIVATE_KEY_HERE
Address = 10.100.0.1/30
ListenPort = 51820
SaveConfig = false

# Enable IP forwarding for this interface
PreUp = sysctl -w net.ipv4.ip_forward=1

# Add route to Site B's network
PostUp = ip route add 192.168.2.0/24 dev wg0

# Configure firewall rules for forwarding
PostUp = iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
PostUp = iptables -A FORWARD -i eth0 -o wg0 -j ACCEPT
PostUp = iptables -A FORWARD -i wg0 -o wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -s 10.100.0.0/30 -o eth0 -j MASQUERADE

# Cleanup rules on shutdown
PreDown = ip route del 192.168.2.0/24 dev wg0 2>/dev/null || true
PreDown = iptables -D FORWARD -i wg0 -o eth0 -j ACCEPT 2>/dev/null || true
PreDown = iptables -D FORWARD -i eth0 -o wg0 -j ACCEPT 2>/dev/null || true
PreDown = iptables -D FORWARD -i wg0 -o wg0 -j ACCEPT 2>/dev/null || true
PreDown = iptables -t nat -D POSTROUTING -s 10.100.0.0/30 -o eth0 -j MASQUERADE 2>/dev/null || true

[Peer]
PublicKey = SITE_B_PUBLIC_KEY_HERE
AllowedIPs = 10.100.0.2/32, 192.168.2.0/24
PersistentKeepalive = 25

Configure secondary site WireGuard server

Configure the secondary site (Site B) with network 192.168.2.0/24. This server connects to the primary site.

[Interface]
PrivateKey = SITE_B_PRIVATE_KEY_HERE
Address = 10.100.0.2/30
SaveConfig = false

# Enable IP forwarding for this interface
PreUp = sysctl -w net.ipv4.ip_forward=1

# Add route to Site A's network
PostUp = ip route add 192.168.1.0/24 dev wg0

# Configure firewall rules for forwarding
PostUp = iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
PostUp = iptables -A FORWARD -i eth0 -o wg0 -j ACCEPT
PostUp = iptables -A FORWARD -i wg0 -o wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -s 10.100.0.0/30 -o eth0 -j MASQUERADE

# Cleanup rules on shutdown
PreDown = ip route del 192.168.1.0/24 dev wg0 2>/dev/null || true
PreDown = iptables -D FORWARD -i wg0 -o eth0 -j ACCEPT 2>/dev/null || true
PreDown = iptables -D FORWARD -i eth0 -o wg0 -j ACCEPT 2>/dev/null || true
PreDown = iptables -D FORWARD -i wg0 -o wg0 -j ACCEPT 2>/dev/null || true
PreDown = iptables -t nat -D POSTROUTING -s 10.100.0.0/30 -o eth0 -j MASQUERADE 2>/dev/null || true

[Peer]
PublicKey = SITE_A_PUBLIC_KEY_HERE
AllowedIPs = 10.100.0.1/32, 192.168.1.0/24
Endpoint = SITE_A_PUBLIC_IP:51820
PersistentKeepalive = 25

Enable permanent IP forwarding

Configure the kernel to forward IP packets between networks permanently. This setting persists across reboots.

net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

Apply the changes immediately:

sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

Set up advanced routing and firewall rules

Configure additional routing rules for better traffic control and security. Create a routing table specifically for WireGuard traffic.

echo "200 wireguard" | sudo tee -a /etc/iproute2/rt_tables

Add advanced routing rules to both sites:

#!/bin/bash
# Advanced routing script for WireGuard site-to-site

# Create custom routing table rules
ip rule add from 10.100.0.0/30 table wireguard priority 100
ip route add 10.100.0.0/30 dev wg0 table wireguard
ip route add 192.168.1.0/24 dev wg0 table wireguard
ip route add 192.168.2.0/24 dev wg0 table wireguard

# Configure firewall rules for enhanced security
iptables -A INPUT -i wg0 -j ACCEPT
iptables -A OUTPUT -o wg0 -j ACCEPT

# Rate limiting for VPN traffic
iptables -A FORWARD -i wg0 -m limit --limit 1000/sec --limit-burst 2000 -j ACCEPT
iptables -A FORWARD -i wg0 -j DROP

# Log dropped packets for monitoring
iptables -A FORWARD -j LOG --log-prefix "WG-FORWARD-DROP: " --log-level 4
sudo chmod +x /etc/wireguard/routing.sh

Configure client network routing

Set up routing on client machines in each network to use the WireGuard gateway for remote network access. Add these routes on client machines or configure them via DHCP.

# On Site A clients (to reach Site B network)
sudo ip route add 192.168.2.0/24 via 192.168.1.1

# On Site B clients (to reach Site A network)
sudo ip route add 192.168.1.0/24 via 192.168.2.1

To make these routes permanent, add them to network configuration:

network:
  version: 2
  ethernets:
    eth0:
      dhcp4: true
      routes:
        - to: 192.168.2.0/24
          via: 192.168.1.1

Start and enable WireGuard services

Start the WireGuard interface on both sites and enable automatic startup.

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo systemctl status wg-quick@wg0

Configure firewall ports

Open the required ports in your firewall for WireGuard communication.

sudo ufw allow 51820/udp
sudo ufw allow from 192.168.1.0/24 to 192.168.2.0/24
sudo ufw allow from 192.168.2.0/24 to 192.168.1.0/24
sudo ufw reload
sudo firewall-cmd --permanent --add-port=51820/udp
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" destination address="192.168.2.0/24" accept'
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.2.0/24" destination address="192.168.1.0/24" accept'
sudo firewall-cmd --reload

Monitor and troubleshoot site-to-site connections

Set up connection monitoring

Create monitoring scripts to track tunnel health and automatically restart failed connections.

#!/bin/bash
# WireGuard site-to-site monitoring script

LOGFILE="/var/log/wireguard-monitor.log"
REMOTE_IP="10.100.0.2"  # Change based on site
REMOTE_NETWORK="192.168.2.1"  # Gateway of remote network

# Function to log messages
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOGFILE
}

# Check tunnel connectivity
ping -c 3 -W 5 $REMOTE_IP > /dev/null 2>&1
if [ $? -ne 0 ]; then
    log_message "Tunnel connectivity failed, checking interface"
    
    # Check if WireGuard interface is up
    if ! ip link show wg0 > /dev/null 2>&1; then
        log_message "WireGuard interface down, restarting"
        systemctl restart wg-quick@wg0
        sleep 10
    fi
    
    # Test remote network connectivity
    ping -c 1 -W 5 $REMOTE_NETWORK > /dev/null 2>&1
    if [ $? -eq 0 ]; then
        log_message "Remote network reachable"
    else
        log_message "Remote network unreachable"
    fi
else
    log_message "Tunnel connectivity OK"
fi

# Log connection statistics
wg show wg0 >> $LOGFILE
sudo chmod +x /etc/wireguard/monitor.sh

Set up automated monitoring

Configure a systemd timer to run the monitoring script every 5 minutes.

[Unit]
Description=WireGuard Site-to-Site Monitor
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/etc/wireguard/monitor.sh
User=root
[Unit]
Description=Run WireGuard Monitor every 5 minutes
Requires=wireguard-monitor.service

[Timer]
OnCalendar=*:0/5
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now wireguard-monitor.timer
sudo systemctl status wireguard-monitor.timer

Configure traffic analysis and logging

Set up detailed logging for traffic analysis and troubleshooting.

# WireGuard logging configuration
:msg, contains, "WG-" /var/log/wireguard.log
& stop
sudo systemctl restart rsyslog
sudo logrotate -d /etc/logrotate.d/wireguard
/var/log/wireguard.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    create 644 syslog syslog
    postrotate
        systemctl reload rsyslog
    endscript
}

Verify your setup

Test the site-to-site connection from both locations to ensure full network connectivity.

# Check WireGuard interface status
sudo wg show
ip addr show wg0

# Test tunnel connectivity between gateways
ping -c 4 10.100.0.2  # From Site A to Site B
ping -c 4 10.100.0.1  # From Site B to Site A

# Test network-to-network connectivity
ping -c 4 192.168.2.1  # From Site A to Site B gateway
ping -c 4 192.168.1.1  # From Site B to Site A gateway

# Check routing table
ip route show table wireguard

# Monitor real-time traffic
sudo tcpdump -i wg0 -n

# Check firewall rules
sudo iptables -L FORWARD -n -v

# Verify monitoring logs
sudo tail -f /var/log/wireguard-monitor.log
Performance test: Use tools like iperf3 to test bandwidth between sites. Install on both networks and run iperf3 -s on one side and iperf3 -c remote_ip on the other.

Common issues

Symptom Cause Fix
Tunnel establishes but no network traffic IP forwarding disabled or missing routes sudo sysctl -w net.ipv4.ip_forward=1 and check routing table
Connection fails after firewall restart Iptables rules not persistent Save rules with sudo iptables-save > /etc/iptables/rules.v4
One-way connectivity only Missing return route on client networks Add static routes on client machines or DHCP server
High latency or packet loss MTU size issues Set MTU to 1420: ip link set mtu 1420 dev wg0
Frequent connection drops NAT timeout or keepalive issues Reduce PersistentKeepalive to 15 seconds
DNS resolution fails across sites DNS server not accessible from remote network Configure DNS forwarding or add remote DNS servers

Next steps

Running this in production?

Want this handled for you? Setting up site-to-site VPN once is straightforward. Keeping it monitored, maintained, and secure across multiple locations with proper failover is the harder part. See how we run infrastructure like this for European teams.

Automated install script

Run this to automate the entire setup

¿Prefiere no gestionarlo usted mismo?

Gestionamos la infraestructura de empresas que dependen del tiempo de actividad. Totalmente gestionada, con un contacto fijo que conoce su entorno.

Tiene un contacto fijo que conoce su entorno

En su escritorio en Róterdam 13:01 · accesible por mensaje, sin formulario de tickets