My WireGuard Multi-Site VPN Setup: Connecting Home Networks
2025-10-13

I have two homes with network gear in each, plus I frequently need to access my NAS and VMs remotely. I wanted a VPN that could tie everything together. After trying OpenVPN and IPSec, I settled on WireGuard -- the config files are short, performance is excellent, and it runs in kernel space. The overall experience is noticeably better than the alternatives.

This post documents the entire process from planning to deployment, including the pitfalls I hit and practical lessons learned.

Requirements and Choosing a Solution

Here's what I needed:

Here's how the options stacked up:

SolutionProsCons
OpenVPNMature, good NAT traversalPoor performance, complex config, userspace
IPSec/IKEv2Standard protocol, native OS supportVery complex config, hard to troubleshoot
TailscaleZero-config, great NAT traversalThird-party dependency, device limits on free tier
WireGuardKernel-space, fast, minimal configNeeds a server with a public IP

I went with WireGuard in a Hub-and-Spoke architecture: one Alibaba Cloud ECS instance with a public IP acts as the relay. All devices connect to this server, which forwards traffic between them. Simple to manage, easy to reason about.

Network Topology

Here's what my network looks like:

flowchart TD
    Internet[Public Internet]

    subgraph Cloud[Alibaba Cloud ECS Relay Server]
        ECS[<strong>ECS Relay Server</strong><br>Public IP: 203.0.113.1<br>VPN IP: 192.168.151.1/24<br>Port: 24356/UDP]
    end

    subgraph HomeA[Home Network A]
        OP1[<strong>OpenWrt Gateway A</strong><br>LAN: 192.168.2.1/24<br>VPN: 192.168.151.2/24]
        OtherA[Other Devices<br>192.168.2.0/24]
    end

    subgraph HomeB[Home Network B]
        OP2[<strong>OpenWrt Gateway B</strong><br>LAN: 192.168.4.1/24<br>VPN: 192.168.151.3/24]
        OtherB[Other Devices<br>192.168.4.0/24]
    end

    subgraph Mobile[Mobile Devices]
        MAC[<strong>MacBook</strong><br>VPN IP: 192.168.151.4/24]
        IPH[<strong>iPhone</strong><br>VPN IP: 192.168.151.5/24]
    end

    Internet -- Internet Connection --> ECS

    ECS -- WireGuard Tunnel<br>PersistentKeepalive: 25 --> OP1
    ECS -- WireGuard Tunnel<br>PersistentKeepalive: 25 --> OP2
    MAC -- WireGuard Tunnel<br>PersistentKeepalive: 25 --> ECS
    IPH -- WireGuard Tunnel<br>PersistentKeepalive: 25 --> ECS

    OP1 -- LAN Network --> OtherA
    OP2 -- LAN Network --> OtherB

Key points:

How WireGuard Works

WireGuard's core is public key cryptography. Each node holds a key pair (private + public). Peers exchange public keys and can then establish encrypted tunnels -- no certificate authority (CA) needed like OpenVPN.

Here's how a connection is established:

sequenceDiagram
    participant S as Server<br/>(Private: S_priv, Public: S_pub)
    participant C as Client<br/>(Private: C_priv, Public: C_pub)

    Note over S,C: Configuration phase: exchange public keys
    S->>S: Add C_pub to config file
    C->>C: Add S_pub to config file

    Note over S,C: Handshake process
    C->>S: 1. Send handshake initiation<br/>(encrypted with S_pub, signed with C_priv)
    S->>S: 2. Decrypt with S_priv, verify with C_pub
    S->>C: 3. Send handshake response<br/>(encrypted with C_pub, signed with S_priv)
    C->>C: 4. Decrypt with C_priv, verify with S_pub

    Note over S,C: Both sides derive session keys
    Note over S,C: 5. Handshake complete, shared secret established

    Note over S,C: Data transfer
    S->>C: Send encrypted data
    C->>S: Send encrypted data

Some technical details worth noting:

Step-by-Step Setup

1. Install WireGuard

Installation is straightforward on every platform:

# Ubuntu / Debian
sudo apt update && sudo apt install wireguard

# OpenWrt (via opkg)
opkg update && opkg install wireguard-tools kmod-wireguard luci-proto-wireguard

For phones and laptops, grab the official WireGuard app from the App Store or Google Play.

2. Generate Key Pairs

Generate a unique key pair for each node:

# Set secure file permissions
umask 077

# Generate and display a key pair (one-liner, no files left behind)
wg genkey | tee /dev/stderr | wg pubkey
# First line of output is the private key, second is the public key

Or do it step by step for clarity:

# Generate private key
wg genkey > server_private.key

# Derive public key from private key
wg pubkey < server_private.key > server_public.key

# View the keys (guard the private key!)
cat server_private.key
cat server_public.key

You need key pairs for: the ECS server, both OpenWrt routers, MacBook, and iPhone. Then cross-configure the public keys -- each node's config includes the public keys of every peer it needs to talk to.

3. Understanding AllowedIPs

This is the most error-prone part of WireGuard configuration, so it deserves its own section. AllowedIPs serves two roles simultaneously:

For example, on the ECS server, setting AllowedIPs = 192.168.151.2/32, 192.168.2.0/24 for Home A's router means:

  1. Traffic bound for 192.168.151.2 or 192.168.2.0/24 goes through this tunnel
  2. Packets arriving through this tunnel must originate from 192.168.151.2 or the 192.168.2.0/24 subnet

4. Central Node Configuration (ECS Server)

This is the core of the architecture -- all traffic flows through here.

Create /etc/wireguard/wg0.conf:

[Interface]
Address = 192.168.151.1/24
ListenPort = 24356
PrivateKey = <server_private_key>

# Enable IP forwarding (allow Linux to route between subnets)
PostUp = sysctl -w net.ipv4.ip_forward=1

# iptables rules: allow forwarding on the WireGuard interface + NAT
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer] # Home Network A - OpenWrt router
PublicKey = <router_a_public_key>
AllowedIPs = 192.168.151.2/32, 192.168.2.0/24

[Peer] # Home Network B - OpenWrt router
PublicKey = <router_b_public_key>
AllowedIPs = 192.168.151.3/32, 192.168.4.0/24

[Peer] # MacBook
PublicKey = <macbook_public_key>
AllowedIPs = 192.168.151.4/32

[Peer] # iPhone
PublicKey = <iphone_public_key>
AllowedIPs = 192.168.151.5/32

Start the service and configure the firewall:

# Enable WireGuard and start on boot
sudo systemctl enable --now wg-quick@wg0

# Open the UDP port
sudo ufw allow 24356/udp

# Check tunnel status
sudo wg show

The wg show output displays each peer's latest handshake time and transfer stats. If a peer's latest handshake is empty, the connection hasn't been established yet -- time to troubleshoot.

5. OpenWrt Router Configuration

The router is the linchpin of this setup -- as a gateway, it lets every device on the LAN transparently access remote networks.

WireGuard config file (you can also configure this through the LuCI web interface):

[Interface]
PrivateKey = <router_private_key>
Address = 192.168.151.2/24

[Peer]
PublicKey = <server_public_key>
Endpoint = 203.0.113.1:24356
AllowedIPs = 192.168.151.0/24, 192.168.4.0/24
PersistentKeepalive = 25

Key configuration points:

In OpenWrt's firewall settings, you also need to:

  1. Add the WireGuard interface to a firewall zone (e.g., create a vpn zone)
  2. Allow forwarding from lan to vpn
  3. Allow forwarding from vpn to lan (if you want remote access to local devices)

6. Mobile Device Configuration

Phone and laptop configs are relatively simple:

[Interface]
PrivateKey = <device_private_key>
Address = 192.168.151.4/24
DNS = 192.168.151.1

[Peer]
PublicKey = <server_public_key>
Endpoint = 203.0.113.1:24356
AllowedIPs = 192.168.151.0/24, 192.168.2.0/24, 192.168.4.0/24
PersistentKeepalive = 25

Here AllowedIPs includes all subnets you need to reach. Don't set it to 0.0.0.0/0 (unless you want all traffic routed through the VPN) -- that would funnel everything through the server, adding latency and wasting bandwidth.

DNS = 192.168.151.1 is optional. Use it if you're running a DNS server (like Pi-hole) on the ECS instance; otherwise, remove it.

7. Verify Connectivity

Once all nodes are configured, test each path:

# Check all peer status on ECS
sudo wg show

# Ping various nodes from a mobile device
ping 192.168.151.1   # ECS server
ping 192.168.151.2   # Router A's VPN IP
ping 192.168.2.1     # Router A's LAN IP
ping 192.168.4.1     # Router B's LAN IP

# Cross-subnet test (from a device on Home A, reach Home B)
ping 192.168.4.100   # A device on Home Network B

# Verify routing table
ip route | grep wg0

Tips and Troubleshooting

Security Hardening

Common Issues

Tunnel won't come up:

  1. Double-check that public keys are correct on both sides (the most common mistake is swapping public and private keys)
  2. Verify both the ECS security group and the system firewall allow the UDP port
  3. Run tcpdump -i eth0 udp port 24356 on the server to see if packets are arriving

Can ping VPN IPs but can't reach the remote LAN:

  1. Check if IP forwarding is enabled: cat /proc/sys/net/ipv4/ip_forward should return 1
  2. Verify iptables FORWARD rules are active
  3. Confirm AllowedIPs includes the remote LAN subnet

OpenWrt LAN devices can't reach remote networks:

  1. Check if OpenWrt firewall allows forwarding from lan to the vpn zone
  2. Verify the routing table has routes to the remote subnets
  3. Run traceroute from the router to see if traffic goes through the WireGuard interface

Persistence and Auto-Start

Make sure WireGuard survives reboots:

# On ECS
sudo systemctl enable wg-quick@wg0

# Make IP forwarding persistent across reboots
echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

Interfaces configured through LuCI on OpenWrt auto-start by default -- no extra steps needed.

Real-World Experience

After running this setup for several months, here are my takeaways:

If you have a lot of peers to manage, hand-editing config files gets tedious. Check out wg-easy -- it provides a clean web UI for managing WireGuard.

Resources