When services run on more than one server — an app server in one location, a database or backup server in another — they should talk over an encrypted private link, not the open internet. A WireGuard tunnel gives each server a private address on a shared network, so you can firewall databases to the tunnel only.
This guide connects two servers, then shows how to route whole subnets (for example, an office network).
The plan
| Server A (app) | Server B (database) | |
|---|---|---|
| Public IP | 203.0.113.10 | 198.51.100.20 |
| Tunnel IP | 10.10.0.1 | 10.10.0.2 |
| WireGuard port | 51820/udp | 51820/udp |
1. Install and create keys (both servers)
apt install -y wireguard
cd /etc/wireguard && umask 077
wg genkey | tee private.key | wg pubkey > public.key
cat public.key # copy to the other server's config
2. Configure Server A
/etc/wireguard/wg0.conf:
[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <A private key>
[Peer]
PublicKey = <B public key>
Endpoint = 198.51.100.20:51820
AllowedIPs = 10.10.0.2/32
PersistentKeepalive = 25
3. Configure Server B
[Interface]
Address = 10.10.0.2/24
ListenPort = 51820
PrivateKey = <B private key>
[Peer]
PublicKey = <A public key>
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.10.0.1/32
PersistentKeepalive = 25
4. Firewall and start (both servers)
Allow WireGuard only from the other server:
ufw allow from 198.51.100.20 to any port 51820 proto udp # on A
ufw allow from 203.0.113.10 to any port 51820 proto udp # on B
systemctl enable --now wg-quick@wg0
Test:
ping -c3 10.10.0.2 # from A
wg show # latest handshake should be recent
5. Use the tunnel
On Server B, make the database listen on the tunnel address only and allow the app server through the tunnel:
# MariaDB: /etc/mysql/mariadb.conf.d/50-server.cnf
bind-address = 10.10.0.2
ufw allow in on wg0 from 10.10.0.1 to any port 3306 proto tcp
The application on Server A connects to 10.10.0.2:3306. Traffic is encrypted end to end and the database isn’t reachable from the internet.
6. Routing whole subnets (site-to-site)
To connect an office network 192.168.10.0/24 (behind a Linux router or a firewall that supports WireGuard) to a VPS:
- On the VPS peer entry for the office, set
AllowedIPs = 10.10.0.3/32, 192.168.10.0/24. - On the office router, set the VPS peer’s
AllowedIPs = 10.10.0.0/24(plus any VPS-side subnets). - Enable forwarding on the office router (
net.ipv4.ip_forward=1) and make sure office machines route10.10.0.0/24via the router.
AllowedIPs acts as both an access list and a routing table: traffic to those addresses goes into the tunnel, and only packets from those addresses are accepted from that peer.
7. More than two servers
For three or more servers, either:
- Full mesh — each server has every other as a peer (simple for a handful of servers), or
- Hub and spoke — all servers connect to one hub that forwards between them (simpler config, one extra hop).
Tools like Netbird, Headscale (Tailscale-compatible) or Nebula automate meshes if you have many machines.
8. Monitor the tunnel
wg show wg0 latest-handshakes
A handshake older than ~3 minutes with keepalive enabled means the link is down. Add a simple check to your monitoring that pings the peer’s tunnel IP.
Latency tip
Tunnel latency equals the network latency between locations. For a database link, keep servers in the same region where possible — for example two USA VPS servers in the same city — and use cross-region tunnels for backups and replication.
Related: personal VPN on a VPS, WireGuard vs OpenVPN and encrypting VPS traffic.