WireGuard is a modern VPN protocol that uses the Noise protocol framework, Curve25519 for key exchange, ChaCha20-Poly1305 for symmetric encryption, and BLAKE2s for hashing. Its implementation in the Linux kernel (mainlined since 5.6) is compact enough to audit and fast enough to saturate a gigabit link on modest hardware. This guide sets up a WireGuard server on a Debian 12 host and adds multiple client peers with both full-tunnel and split-tunnel configurations.
1. Install WireGuard
WireGuard is in the main Debian and Ubuntu repositories:
apt update && apt install -y wireguard wireguard-tools
Confirm the kernel module loads:
modprobe wireguard lsmod | grep wireguard
2. Generate server keys
wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key chmod 600 /etc/wireguard/server_private.key cat /etc/wireguard/server_public.key
3. Server configuration
Create /etc/wireguard/wg0.conf:
[Interface] Address = 10.8.0.1/24 ListenPort = 51820 PrivateKey = <contents of server_private.key> # NAT: replace eth0 with your outbound interface 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
Enable IP forwarding:
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf sysctl -p
Open the firewall port and start the interface:
ufw allow 51820/udp systemctl enable --now wg-quick@wg0 wg show
4. Generate and add peer keys
Repeat for each client. On the client machine (or generate on the server and transfer securely):
wg genkey | tee laptop_private.key | wg pubkey > laptop_public.key wg genpsk > laptop_preshared.key # optional extra layer
Add the peer to the server’s wg0.conf:
[Peer] # laptop — Priya's ThinkPad PublicKey = <laptop_public.key> PresharedKey = <laptop_preshared.key> AllowedIPs = 10.8.0.2/32
systemctl reload wg-quick@wg0 # or live-reload without restart: wg addpeer <pubkey> preshared-key <pskfile> allowed-ips 10.8.0.2/32
5. Client configuration: full tunnel
On the client machine, create /etc/wireguard/wg0.conf:
[Interface] Address = 10.8.0.2/32 PrivateKey = <contents of laptop_private.key> DNS = 10.8.0.1 # Pi-hole / Unbound on the server [Peer] PublicKey = <server_public.key> PresharedKey = <laptop_preshared.key> Endpoint = vpn.yourdomain.net:51820 AllowedIPs = 0.0.0.0/0, ::/0 # full tunnel PersistentKeepalive = 25
wg-quick up wg0 ping 10.8.0.1
PersistentKeepalive = 25 sends a keepalive packet every 25 seconds.
This is necessary when the client is behind NAT and the server needs to
be able to reach it. Omit it if the client always initiates connections.
6. Split-tunnel configuration
For a client that should only route homelab subnets through the VPN (and
use its local network for everything else), change AllowedIPs:
[Peer] ... AllowedIPs = 10.8.0.0/24, 192.168.10.0/24, 192.168.20.0/24 # Omit PersistentKeepalive if client-initiated only
The AllowedIPs field doubles as a routing table: WireGuard will
route packets matching those subnets into the tunnel. Everything else goes
out the default route.
7. Multiple peers example
A server config with two peers:
[Interface] Address = 10.8.0.1/24 ListenPort = 51820 PrivateKey = <SERVER_PRIVATE_KEY> PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE [Peer] # laptop PublicKey = <LAPTOP_PUB> PresharedKey = <LAPTOP_PSK> AllowedIPs = 10.8.0.2/32 [Peer] # phone PublicKey = <PHONE_PUB> PresharedKey = <PHONE_PSK> AllowedIPs = 10.8.0.3/32
8. Verifying the tunnel
wg show # On client: curl -4 ifconfig.me # should return server's public IP on full tunnel traceroute 192.168.10.1 # should route through VPN on split tunnel
9. QR codes for mobile clients
Install qrencode and print a QR code the WireGuard mobile app
can scan:
apt install -y qrencode qrencode -t ansiutf8 < /etc/wireguard/client-phone.conf
Do this in a terminal session you trust; the QR encodes the private key.