Why WireGuard?
OpenVPN has been the default choice for self-hosted VPNs for years, but it shows its age: complex configuration, slow handshakes, and a massive codebase (~600,000 lines) that's difficult to audit. WireGuard solves all of this. The entire codebase is around 4,000 lines of code, making it far easier to audit for security vulnerabilities. It uses modern cryptography (Curve25519, ChaCha20, Poly1305) by design - there's no negotiation or cipher selection that an attacker can downgrade.
Performance-wise, WireGuard benchmarks consistently faster than OpenVPN and IPsec, especially on mobile devices where battery and latency matter. Reconnection after a network change (Wi-Fi to mobile data) is nearly instant compared to the multi-second handshakes of traditional VPN protocols.
Prerequisites
You'll need a Linux server with a public IP address. WireGuard is included in the Linux kernel from version 5.6 (Ubuntu 20.04+, Debian 11+). On older kernels it's available as a DKMS module. You'll also need the wireguard-tools package for wg and wg-quick.
# Debian / Ubuntu
apt update && apt install wireguard
# Check your kernel version
uname -r
Generate Keys
WireGuard uses asymmetric key pairs - a private key stays on the host, the public key is shared with peers. Generate separate key pairs for the server and each client.
# Generate server keys (store them securely)
wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key
# Generate client keys
wg genkey | tee /etc/wireguard/client1_private.key | wg pubkey > /etc/wireguard/client1_public.key
# Restrict permissions on private keys
chmod 600 /etc/wireguard/server_private.key /etc/wireguard/client1_private.key
# Read the keys - you'll need them in the config files
cat /etc/wireguard/server_private.key
cat /etc/wireguard/server_public.key
cat /etc/wireguard/client1_private.key
cat /etc/wireguard/client1_public.key
Server Configuration
Create /etc/wireguard/wg0.conf on the server. Replace the key values and your server's public IP address:
[Interface]
# The VPN server's private key
PrivateKey = <SERVER_PRIVATE_KEY>
# VPN subnet - the server gets .1, clients get .2, .3, etc.
Address = 10.0.0.1/24
# UDP port WireGuard listens on
ListenPort = 51820
# NAT rules to allow client traffic to reach the internet
# Replace eth0 with your server's actual network interface
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# Client 1 - add one [Peer] block per client
[Peer]
PublicKey = <CLIENT1_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32
Enable IP Forwarding and Open the Firewall
For the server to route client traffic to the internet, the kernel must allow IP forwarding. Edit /etc/sysctl.conf and uncomment (or add):
net.ipv4.ip_forward = 1
Apply it: sysctl -p
Then open the WireGuard UDP port in UFW:
ufw allow 51820/udp
ufw reload
Start and Enable WireGuard
# Start the interface
wg-quick up wg0
# Enable on boot
systemctl enable wg-quick@wg0
# Verify the interface is up
wg show wg0
Client Configuration
Create a config file on the client machine. On Linux this goes in /etc/wireguard/wg0.conf. On Windows and macOS, paste it into the WireGuard app. For a mobile device, generate the config and display it as a QR code with qrencode.
[Interface]
PrivateKey = <CLIENT1_PRIVATE_KEY>
Address = 10.0.0.2/24
DNS = 1.1.1.1
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = YOUR.SERVER.IP:51820
# Route all traffic through the VPN (full tunnel)
# Use 10.0.0.0/24 only for a split tunnel (VPN subnet only)
AllowedIPs = 0.0.0.0/0, ::/0
# Keep the connection alive through NAT
PersistentKeepalive = 25
[Interface] section to block all non-VPN traffic if the tunnel drops: PostUp = iptables -I OUTPUT ! -o wg0 -m mark ! --mark $(wg show wg0 fwmark) -m addrtype ! --dst-type LOCAL -j REJECT and PreDown = iptables -D OUTPUT ! -o wg0 -m mark ! --mark $(wg show wg0 fwmark) -m addrtype ! --dst-type LOCAL -j REJECTVerify the Connection
# On the client, bring up the tunnel
wg-quick up wg0
# Check your public IP - should now show the server's IP
curl https://ifconfig.me
# On the server, check connected peers
wg show wg0
# Test latency to the server's VPN IP
ping 10.0.0.1
A successful setup shows the client's handshake time in wg show output and routes all traffic through the server. Adding more clients is simply a matter of generating new key pairs, adding a [Peer] block to the server config, and creating a client config with the assigned IP.