Roaming and keepalives
WireGuard doesn't have connections that can drop. It sends packets, and knows each peer by its key, not by its address. That makes two everyday situations painless.
Roaming
When alice's laptop moves from one network to another, her address changes. With WireGuard, nothing needs to reconnect: her next packet arrives from the new address, the server checks it's correctly encrypted with her key, and simply updates her endpoint to the new address. In the lab, alice changed address in the middle of a session; her pings kept working, and the server's view of her endpoint switched from 198.51.100.21 to 198.51.100.31, still on the same handshake.
Keepalives
WireGuard is silent when there's nothing to send. That's efficient, but a NAT router or stateful firewall between a user and the server forgets the mapping after a while of silence, and then the server can't reach the user until they send something. PersistentKeepalive = 25 on the user's side sends a tiny packet every 25 seconds to keep the path open. Set it on clients behind NAT (most of them); a server with a fixed public address doesn't need it.
The capture on the server shows bob's 32-byte keepalives every 25 seconds while he's idle. It also shows something else: a 148-byte handshake initiation and a 92-byte response. WireGuard re-keys automatically every two minutes while a tunnel is in use, so even a stolen session key is useless shortly after.
root@vpn:~# wg show | sed -n "/hwCdM7/,/^$/p" | grep -E "peer|endpoint|handshake"
peer: hwCdM7ImFlIKfF0sQHngu9THMAenVRLSD1JYIrqR2Qw=
endpoint: 198.51.100.21:42934
latest handshake: 12 seconds ago
root@alice:~# ip addr del 198.51.100.21/24 dev eth0 && ip addr add 198.51.100.31/24 dev eth0 && ip route add default via 198.51.100.1 && ip -br -4 addr show eth0
eth0@if7649 UP 198.51.100.31/24
root@alice:~# ping -c 2 -W 1 10.10.0.20 | tail -2
2 packets transmitted, 2 received, 0% packet loss, time 1060ms
rtt min/avg/max/mdev = 0.276/0.555/0.834/0.279 ms
root@vpn:~# wg show | sed -n "/hwCdM7/,/^$/p" | grep -E "peer|endpoint|handshake"
peer: hwCdM7ImFlIKfF0sQHngu9THMAenVRLSD1JYIrqR2Qw=
endpoint: 198.51.100.31:42934
latest handshake: 14 seconds ago- Lines 3–4: Before: alice at 198.51.100.21.
- Lines 6–7: Her laptop moves to a new address, as on a new Wi-Fi network.
- Lines 9–11: No reconnect, no lost pings.
- Lines 15–16: The server simply updated her endpoint. Same port, same handshake.
root@vpn:~# timeout 55 tcpdump -lnni eth0 "udp port 51820 and host 198.51.100.22"
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
06:48:49.411172 IP 198.51.100.22.34037 > 203.0.113.10.51820: UDP, length 32
06:49:14.499762 IP 198.51.100.22.34037 > 203.0.113.10.51820: UDP, length 32
06:49:39.587647 IP 198.51.100.22.34037 > 203.0.113.10.51820: UDP, length 32
06:49:39.589945 IP 198.51.100.22.34037 > 203.0.113.10.51820: UDP, length 148
06:49:39.591002 IP 203.0.113.10.51820 > 198.51.100.22.34037: UDP, length 92
06:49:39.591725 IP 198.51.100.22.34037 > 203.0.113.10.51820: UDP, length 32
6 packets captured
6 packets received by filter
0 packets dropped by kernel- Lines 4–6: A 32-byte keepalive every 25 seconds.
- Lines 7–9: A re-key: a 148-byte handshake initiation, a 92-byte response and a confirming packet. Session keys never live long.
Check your understanding
A user on hotel Wi-Fi says the VPN works when she uses it, but the office can't reach her laptop after a few minutes of quiet. What fixes it?
- PersistentKeepalive = 25 in her config.
- A larger MTU.
- Restarting wg-quick@wg0 on the server.
- Adding her hotel address to the server's AllowedIPs.