← ALL POSTS
CLOUD SETUP & MIGRATION STEP-BY-STEP GUIDE

Set up a WireGuard VPN between two offices

Two offices, one network. Staff at the branch open the file server at headquarters like it is down the hall, printers and internal apps work from both sides, and nothing is exposed to the internet. That is a site-to-site VPN, and WireGuard is the modern way to build one: it is built into the Linux kernel, the config for a whole tunnel fits on one screen, and it is fast enough that you will not notice it is there. By the end of this guide you will have an encrypted tunnel between two office networks with traffic routing both ways.

Prerequisites

Install WireGuard on both gateways:

sudo apt update
sudo apt install wireguard

Step 1: Generate keys on both gateways

WireGuard identifies peers by key pairs, no usernames or certificates. On each gateway, generate a private and public key:

wg genkey | sudo tee /etc/wireguard/private.key
sudo chmod 600 /etc/wireguard/private.key
sudo cat /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key

Do this on Office A's gateway and again on Office B's. You will end up with four keys. Each side keeps its own private key secret and shares only its public key with the other side. The tunnel itself gets its own tiny subnet: we will give Office A's gateway 10.100.0.1 and Office B's 10.100.0.2.

Step 2: Configure Office A (the reachable site)

Create /etc/wireguard/wg0.conf on Office A's gateway:

[Interface]
Address = 10.100.0.1/24
ListenPort = 51820
PrivateKey = <OFFICE_A_PRIVATE_KEY>

[Peer]
# Office B
PublicKey = <OFFICE_B_PUBLIC_KEY>
AllowedIPs = 10.100.0.2/32, 192.168.2.0/24

AllowedIPs is the line people get wrong, because it does two jobs at once. It is both a routing table entry ("send traffic for these networks through this peer") and a filter ("accept traffic from this peer only if it comes from these networks"). Office A's config says: to reach Office B's tunnel address or anything on Office B's LAN, use this tunnel. It is not related to what traffic is allowed in the firewall sense of an allowlist of clients.

Step 3: Configure Office B

Create /etc/wireguard/wg0.conf on Office B's gateway:

[Interface]
Address = 10.100.0.2/24
PrivateKey = <OFFICE_B_PRIVATE_KEY>

[Peer]
# Office A
PublicKey = <OFFICE_A_PUBLIC_KEY>
Endpoint = vpn.example.com:51820
AllowedIPs = 10.100.0.1/32, 192.168.1.0/24
PersistentKeepalive = 25

Two things are different on this side. Endpoint is Office A's public IP or DNS name; Office B initiates the connection, which is why Office B does not need to be reachable from the internet. PersistentKeepalive = 25 sends a tiny packet every 25 seconds so the NAT router at Office B keeps the connection alive. Without it, the tunnel goes quiet, the router forgets the mapping, and Office A can no longer reach Office B until B sends traffic again. On a site-to-site link behind NAT, always set it.

Step 4: Enable IP forwarding

Each gateway needs to pass packets between its LAN and the tunnel, which Linux refuses to do by default. On both gateways:

echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

Skipping this is the classic "the gateways can ping each other but nothing else works" bug.

Step 5: Open the firewall and bring the tunnel up

On Office A (the listening side), allow WireGuard's port. With ufw:

sudo ufw allow 51820/udp

If Office A's gateway sits behind an office router, also forward UDP 51820 on that router to the gateway's LAN address. Then start the tunnel on both sides and set it to survive reboots:

sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0

Step 6: Tell the LANs about each other

Machines at Office A need to know that 192.168.2.0/24 lives behind the gateway. The clean fix is one static route on each office's main router: at Office A, route 192.168.2.0/24 via the gateway's LAN IP; at Office B, route 192.168.1.0/24 via its gateway's LAN IP. Almost every business router has a static routes page. Without this step, only the gateways themselves can cross the tunnel.

Verify it

Check the tunnel is alive on either gateway:

sudo wg show

You want to see a latest handshake line with a recent time and the transfer counters climbing. Then prove the routing end to end:

# From Office A's gateway, ping B's tunnel address, then a LAN host at B
ping 10.100.0.2
ping 192.168.2.10

# From a regular desktop at Office A
ping 192.168.2.10

If the gateways ping but desktops do not, recheck Step 4 (forwarding) and Step 6 (static routes). If there is no handshake at all, it is almost always the endpoint address, the port forward, or a mixed-up key. Once the handshake shows and pings cross both LANs, you are done: reboot a gateway to confirm it comes back on its own, and the two offices are one network from then on. If you would rather have this built, tested, and documented for you, tunnel jobs like this are routine work for us.

Stuck on this, or want it done for you? That's the job.

Email us →
RELATED READING
Host a static site on Cloudflare Workers Step-By-Step Guide
First 30 minutes on a new Ubuntu server Step-By-Step Guide
Set up uptime monitoring and alerts that wake the right person Step-By-Step Guide
Do you still need a VPN? Explainer
Set up a pfSense firewall for a small office Step-By-Step Guide
NO FORMS. JUST EMAIL.
mason@hurbs.io
or (832) 457-4317, LA and Houston