Configure WireGuard Site-to-Site VPN on Linux (2025 Guide)

Why WireGuard for a Site-to-Site VPN?

WireGuard has become a go-to VPN choice for modern Linux networks because it is fast, lightweight, and easier to audit than many legacy VPN stacks. For a site-to-site setup (connecting two networks, like HQ and a branch office), WireGuard works especially well: it uses simple public-key cryptography, keeps the configuration small, and performs efficiently even on modest hardware or small cloud VPS instances.

In this tutorial, you will build a reliable site-to-site WireGuard VPN between two Linux gateways. The steps are written for current distributions such as Ubuntu 22.04/24.04 or Debian 12, but the process is similar on most Linux systems. The goal is to route traffic between two private subnets securely, without exposing internal services to the public internet.

Network Example (Adjust to Your Environment)

This guide uses a clear example so you can map it to your own network. Site A (HQ) has LAN 192.168.10.0/24 and a Linux gateway with public IP A_PUBLIC_IP. Site B (Branch) has LAN 192.168.20.0/24 and a Linux gateway with public IP B_PUBLIC_IP. WireGuard will use a dedicated tunnel network: 10.99.0.0/24, where Site A will be 10.99.0.1 and Site B will be 10.99.0.2.

Prerequisites: UDP port access (commonly 51820), root or sudo privileges, and the gateways must be able to reach each other over the internet. Make sure you are not using overlapping LAN ranges (for example, both sides using 192.168.1.0/24). Overlapping subnets are a common reason site-to-site VPN routing fails.

Step 1: Install WireGuard

On both gateways, install WireGuard:

Ubuntu/Debian:

sudo apt update && sudo apt install -y wireguard

If you are using another distro, install the equivalent package (for example, on RHEL-based systems you may use EPEL or distro repositories depending on your version).

Step 2: Generate Keys (Both Sides)

WireGuard uses a private/public key pair per node. On Site A:

umask 077
wg genkey | tee /etc/wireguard/privatekey | wg pubkey > /etc/wireguard/publickey

Repeat the same on Site B. Then read the public keys so you can paste them into the peer configuration:

cat /etc/wireguard/publickey

Keep private keys private. Do not copy them into tickets, chat, or documentation.

Step 3: Create the WireGuard Config on Site A

Create /etc/wireguard/wg0.conf on Site A:

[Interface]
Address = 10.99.0.1/24
ListenPort = 51820
PrivateKey = SITE_A_PRIVATE_KEY
PostUp = sysctl -w net.ipv4.ip_forward=1; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT

[Peer]
PublicKey = SITE_B_PUBLIC_KEY
AllowedIPs = 10.99.0.2/32, 192.168.20.0/24
Endpoint = B_PUBLIC_IP:51820
PersistentKeepalive = 25

The key line for site-to-site routing is AllowedIPs. It tells Site A that traffic for the branch LAN (192.168.20.0/24) should be routed into the tunnel toward Site B.

Step 4: Create the WireGuard Config on Site B

Create /etc/wireguard/wg0.conf on Site B:

[Interface]
Address = 10.99.0.2/24
ListenPort = 51820
PrivateKey = SITE_B_PRIVATE_KEY
PostUp = sysctl -w net.ipv4.ip_forward=1; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT

[Peer]
PublicKey = SITE_A_PUBLIC_KEY
AllowedIPs = 10.99.0.1/32, 192.168.10.0/24
Endpoint = A_PUBLIC_IP:51820
PersistentKeepalive = 25

Step 5: Enable IP Forwarding Permanently

WireGuard can come up fine but routing will still fail if forwarding is disabled after reboot. On both gateways:

echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard-forward.conf
sudo sysctl --system

Step 6: Start and Enable the Tunnel

Bring up the tunnel on both sides:

sudo systemctl enable --now wg-quick@wg0

Check status and handshake:

sudo wg show

You should see a recent handshake time and data counters increasing when traffic flows.

Step 7: Firewall and Routing Checks

If you cannot reach the remote LAN, start with the basics: confirm UDP port 51820 is open on both public interfaces, and confirm that the LAN hosts use the Linux gateway as their default route (or have a route to the opposite LAN via the gateway). A very common issue is that the gateways can ping each other over the tunnel, but client machines cannot, because the clients do not know where to send the return traffic.

Test from the gateways first: ping Site B tunnel IP from Site A (ping 10.99.0.2) and then ping a host on the branch LAN. If gateway-to-gateway works but LAN-to-LAN fails, you likely need to add static routes on your LAN routers or ensure the gateways are the default routers for their subnets.

Step 8: Quick Troubleshooting Tips

No handshake: verify the endpoint IP/port, confirm public keys match the correct peers, and check upstream NAT or security groups. Handshake but no LAN access: confirm IP forwarding is enabled, confirm AllowedIPs includes the remote LAN, and confirm routes on LAN clients. Intermittent connectivity: keep PersistentKeepalive = 25 on at least one side when NAT is involved.

Once the tunnel is stable, you can harden it further by limiting which ports are allowed between sites, moving from iptables rules to a dedicated firewall policy, and documenting the exact subnets in AllowedIPs so the VPN stays predictable as your network grows.

Comments