Managing users
Adding and removing users is where WireGuard's simplicity shines, and where one convenient habit can take every user offline at once.
Adding a user, live
The usual method is to add the new [Peer] block to wg0.conf, then apply it to the running interface without a restart:
wg syncconf wg0 <(wg-quick strip wg0)
syncconf changes only what differs, so existing users' sessions aren't interrupted. But it makes the interface match the file exactly, and this server's file deliberately has no PrivateKey (it's loaded by PostUp). In the lab, syncconf added carol, and also removed the server's private key: wg show wg0 private-key said (none), and every user's tunnel stopped working, with no error from any command.
So pick one approach and stick to it:
- Private key in
wg0.conf(root-only,chmod 600):wg syncconfis safe. This is the common setup. - Private key in its own file, loaded by
PostUp: change peers withwg setinstead, which never touches the key, and edit the file too so the change survives a restart. If you do usesyncconf, follow it withwg set wg0 private-key …straight away.
Removing a user
wg set wg0 peer <their public key> remove takes effect immediately: bob's very next packet was dropped. There's no revocation list to publish and nothing to restart. Remove their block from wg0.conf too, or they'll be back after the next reboot. bob's own client still shows a recent handshake, because from his side nothing told it otherwise; it just stops getting answers.
Preshared keys
For an extra layer, each peer pair can share a symmetric key, made with wg genpsk and added as PresharedKey on both ends. It's mixed into the handshake, which protects today's traffic against a future attacker with a quantum computer that could break the public-key exchange. It's a secret, so treat it like a private key.
[Peer]
# carol
PublicKey = P+TMulXP07Ldwcf65Rx1CvXDOxK57C3HWV5aY5oDHEo=
AllowedIPs = 10.9.0.12/32root@vpn:~# cat peer-carol.conf >> /etc/wireguard/wg0.conf && wg syncconf wg0 <(wg-quick strip wg0) && wg show wg0 peers
hwCdM7ImFlIKfF0sQHngu9THMAenVRLSD1JYIrqR2Qw=
8BpkL72eT8cHe8HTgKE8yWD464E0FVbQGlQ1FPi0tnc=
sh5Ul4rjT56yt7ZYYVSrpJvxEIqKFx/HxkeICJIgGws=
P+TMulXP07Ldwcf65Rx1CvXDOxK57C3HWV5aY5oDHEo=
root@carol:~# wg-quick up wg0 >/dev/null 2>&1 && ping -c 2 -W 1 10.10.0.20 | tail -2
2 packets transmitted, 0 received, 100% packet loss, time 1043ms
root@vpn:~# wg show | sed -n "/hwCdM7/,/^$/p" | grep -E "peer|handshake"
peer: hwCdM7ImFlIKfF0sQHngu9THMAenVRLSD1JYIrqR2Qw=
latest handshake: 1 minute, 42 seconds ago
root@vpn:~# wg show wg0 private-key
(none)
root@alice:~# ping -c 2 -W 1 10.10.0.20 | tail -2
2 packets transmitted, 0 received, 100% packet loss, time 1035ms- Lines 1–5: carol is added; the four peers look right.
- Lines 7–8: But carol can't connect…
- Lines 11–13: …and alice's last handshake is getting old.
- Lines 15–16: The server's private key is gone: the file didn't have one, so
syncconfremoved it. - Lines 18–19: Every user is now cut off.
root@vpn:~# wg set wg0 private-key /etc/wireguard/server.key && wg show wg0 public-key
/S03YCIRbPGjlhE53GYhxFEh8LVo/Joel3BkEgWeeXk=
root@carol:~# ping -c 2 -W 1 10.10.0.20 | tail -2
2 packets transmitted, 0 received, 100% packet loss, time 1027ms
root@alice:~# ping -c 2 -W 1 10.10.0.20 | tail -2
2 packets transmitted, 2 received, 0% packet loss, time 1027ms
rtt min/avg/max/mdev = 0.351/0.653/0.955/0.302 ms
root@vpn:~# wg show | grep -E "^peer|handshake"
peer: P+TMulXP07Ldwcf65Rx1CvXDOxK57C3HWV5aY5oDHEo=
latest handshake: 2 seconds ago
peer: hwCdM7ImFlIKfF0sQHngu9THMAenVRLSD1JYIrqR2Qw=
latest handshake: 4 seconds ago
peer: 8BpkL72eT8cHe8HTgKE8yWD464E0FVbQGlQ1FPi0tnc=
latest handshake: 55 seconds ago
peer: sh5Ul4rjT56yt7ZYYVSrpJvxEIqKFx/HxkeICJIgGws=
latest handshake: 1 minute, 7 seconds ago
root@carol:~# ping -c 2 -W 1 10.10.0.20 | tail -2
2 packets transmitted, 2 received, 0% packet loss, time 1069ms
rtt min/avg/max/mdev = 0.369/0.529/0.689/0.160 ms- Lines 1–2: The key is back, and so is the public key users expect.
- Lines 4–5: carol's first try still fails: her tunnel is waiting a few seconds before retrying its handshake.
- Lines 8–10: alice works straight away.
- Lines 12–20: Fresh handshakes all round.
- Lines 22–24: And carol's retry gets through.
root@bob:~# ping -c 2 -W 1 10.10.0.20 | tail -2
2 packets transmitted, 2 received, 0% packet loss, time 1014ms
rtt min/avg/max/mdev = 0.611/0.691/0.772/0.080 ms
root@vpn:~# wg set wg0 peer 8BpkL72eT8cHe8HTgKE8yWD464E0FVbQGlQ1FPi0tnc= remove && wg show wg0 peers && wg show wg0 public-key
hwCdM7ImFlIKfF0sQHngu9THMAenVRLSD1JYIrqR2Qw=
sh5Ul4rjT56yt7ZYYVSrpJvxEIqKFx/HxkeICJIgGws=
P+TMulXP07Ldwcf65Rx1CvXDOxK57C3HWV5aY5oDHEo=
/S03YCIRbPGjlhE53GYhxFEh8LVo/Joel3BkEgWeeXk=
root@bob:~# ping -c 2 -W 1 10.10.0.20 | tail -2
2 packets transmitted, 0 received, 100% packet loss, time 1055ms
root@bob:~# wg show | grep -E "handshake|transfer"
latest handshake: 4 seconds ago
transfer: 1.07 KiB received, 2.19 KiB sent
root@vpn:~# grep -c "^\[Peer\]" /etc/wireguard/wg0.conf; grep "^# " /etc/wireguard/wg0.conf
3
# /etc/wireguard/wg0.conf on the VPN server (systemctl enable --now wg-quick@wg0)
# The private key stays in its own root-only file, not in this config.
# One [Peer] per user. AllowedIPs is both the route to them and the only
# source addresses accepted from them.
# alice
# branch: a router, with the branch LAN behind it
# carol- Lines 1–3: bob works.
- Lines 5–9: One command removes his key; the server's own key is untouched.
- Lines 11–12: His very next ping fails.
- Lines 15–17: His own client can't tell: from its side, the last handshake was fine.
- Lines 19–27: bob's block also removed from the file, so he stays gone after a restart.
Check your understanding
Your server's wg0.conf has the private key in PostUp, not as PrivateKey. How should you add a new user without a restart?
wg set wg0 peer <key> allowed-ips …, and add the block to the file.wg syncconf wg0 <(wg-quick strip wg0)on its own.systemctl reload wg-quick@wg0.- Edit the file; WireGuard rereads it automatically.
Check your understanding
How quickly does removing a user's peer stop them, compared with revoking an OpenVPN certificate?
- Immediately: their next packet has no key to match.
- At their next handshake, up to two minutes later.
- When the server restarts.
- When their client is restarted.