Build a WireGuard VPN
Configure the WireGuard peers
Give each interface its private address and key, then authorize the exact address owned by the other peer.
Each peer needs a config file with two parts. An [Interface] section describes the peer itself. A [Peer] section describes who it talks to.
Let’s start with the server. Create /etc/wireguard/wg0.conf:
[Interface]
Address = 10.14.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY
[Peer]
PublicKey = LAPTOP_PUBLIC_KEY
AllowedIPs = 10.14.0.2/32
The server describes itself with its VPN address, its listening port, and its private key. The [Peer] block authorizes the laptop. Traffic from that public key may claim the source address 10.14.0.2, and traffic destined to 10.14.0.2 goes to that peer.
Now the laptop. Create the matching file:
[Interface]
Address = 10.14.0.2/24
PrivateKey = LAPTOP_PRIVATE_KEY
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.14.0.0/24
PersistentKeepalive = 25
The two files are not mirror images, and the differences are worth reading twice.
Only the laptop has an Endpoint, because only the laptop knows where to find the other side. The server learns the laptop’s address from its first authenticated packet. That is what lets the laptop roam from home Wi-Fi to a phone hotspot without any config change.
The laptop’s AllowedIPs covers the whole 10.14.0.0/24, so the entire VPN subnet goes through the tunnel. The server’s AllowedIPs is exactly /32, one address, because that laptop owns one address and nothing more.
PersistentKeepalive = 25 sends a tiny packet every 25 seconds. That keeps the NAT mapping in the laptop’s home router alive, so the server can still reach the laptop when the laptop has been quiet for a while.
AllowedIPs is the setting people misread. It does two jobs at once. It is a routing rule: which destinations go to this peer. And it is a source filter: which addresses this peer is allowed to use. Keep it narrow on the server, one /32 per client, and wider on the client.
Replace the placeholders with the keys you generated. SERVER_PRIVATE_KEY is the content of server-private.key, LAPTOP_PUBLIC_KEY is the content of laptop-public.key, and so on. Then lock down the file, because it contains a private key:
sudo chmod 600 /etc/wireguard/wg0.conf
Do this on both machines.
The classic mistake here is swapped keys. Each [Peer] block must contain the other side’s public key. Paste a peer’s own public key into its config and the handshake never completes. wg show will list the peer with no latest handshake line at all. When you see that, re-check every key before you touch anything else.
Lesson completed