WireGuard is the fastest and simplest VPN protocol available. Setting it up on your own VPS gives you complete control over your privacy - no third-party VPN provider needed.
Why Self-Hosted VPN
- Full control - No logging by a VPN provider, you own the server
- Speed - WireGuard is faster than OpenVPN with less CPU overhead
- Cost effective - A single VPS handles VPN for multiple devices
- Custom routing - Route only specific traffic through the VPN
- Static IP - Useful for accessing services that whitelist IPs
Installation
Server Side (Ubuntu/Debian)
# Install WireGuard
apt update && apt install -y wireguard
# Generate server keys
wg genkey | tee /etc/wireguard/private.key | wg pubkey > /etc/wireguard/public.key
chmod 600 /etc/wireguard/private.key
Server Configuration
Create /etc/wireguard/wg0.conf:
[Interface]
PrivateKey = <server-private-key>
Address = 10.0.0.1/24
ListenPort = 51820
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]
PublicKey = <client-public-key>
AllowedIPs = 10.0.0.2/32
Enable IP Forwarding
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p
Start WireGuard
systemctl enable --now wg-quick@wg0
Open Firewall Port
ufw allow 51820/udp
Client Configuration
Generate Client Keys
wg genkey | tee client-private.key | wg pubkey > client-public.key
Client Config File
[Interface]
PrivateKey = <client-private-key>
Address = 10.0.0.2/24
DNS = 1.1.1.1
[Peer]
PublicKey = <server-public-key>
Endpoint = your-vps-ip:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Import this configuration file into the WireGuard client on your device (available for Windows, macOS, Linux, iOS, Android).
Adding More Clients
For each new client:
- Generate a new key pair
- Assign a unique IP (10.0.0.3, 10.0.0.4, etc.)
- Add a [Peer] section to the server config
- Create a client config file
- Reload WireGuard:
wg syncconf wg0 <(wg-quick strip wg0)
DNS Leak Prevention
To prevent DNS leaks, either:
- Set DNS in the client config to a privacy-respecting resolver (1.1.1.1, 9.9.9.9)
- Run your own DNS resolver on the VPS (Unbound or Pi-hole)
Split Tunneling
Route only specific traffic through the VPN by adjusting AllowedIPs:
# Route only internal network through VPN
AllowedIPs = 10.0.0.0/24
# Route everything except local network through VPN
AllowedIPs = 0.0.0.0/0, ::/0
Performance
WireGuard is extremely lightweight:
- Uses ~1-2% CPU on a 1-core VPS
- Adds ~50-80ms overhead at most
- Handles multiple clients easily on a small VPS
A VPS with 1GB RAM and 1 CPU core comfortably runs WireGuard for 10+ simultaneous clients.
Security Best Practices
- Keep your private keys secure - never share them
- Use a firewall to restrict WireGuard port to expected client IPs if possible
- Regularly update WireGuard to get security patches
- Rotate keys periodically for maximum security
- Monitor connection logs for unauthorized access attempts
Self-hosted WireGuard VPN on a VPS gives you privacy, speed, and control that commercial VPN services can't match.
When WireGuard is the right tool
WireGuard is a kernel-level VPN designed for two things: low overhead, and config simplicity. Pick it when you need:
- Secure remote access to your VPS / homelab.
- Site-to-site VPN between two servers.
- A privacy VPN for your laptop / phone.
- CGNAT bypass (your home is on a shared IP, you need a public-facing endpoint).
Skip it for: client-to-many anonymity (use Tor or a paid service), corporate IDP integration (use OpenVPN or a managed solution).
Install on a Debian/Ubuntu VPS
sudo apt install -y wireguard wireguard-tools
cd /etc/wireguard
umask 077
wg genkey | tee server.key | wg pubkey > server.pub
/etc/wireguard/wg0.conf (server)
[Interface]
Address = 10.13.13.1/24
ListenPort = 51820
PrivateKey = <contents of server.key>
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
[Peer]
PublicKey = <client public key>
AllowedIPs = 10.13.13.2/32
/etc/wireguard/wg0.conf (client)
[Interface]
Address = 10.13.13.2/32
PrivateKey = <client private key>
DNS = 1.1.1.1
[Peer]
PublicKey = <server public key>
Endpoint = your.vps.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
Enabling forwarding
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-wg.conf
sudo sysctl --system
Bring it up
sudo systemctl enable --now wg-quick@wg0
sudo wg
wg shows handshake status. A working peer shows "latest handshake" updating every 2-3 minutes.
Common breakage
| Symptom | Cause | Fix |
|---|---|---|
| Handshake stays at "(none)" | UDP 51820 blocked at firewall | open 51820/udp |
| Client connects but no internet | IP forwarding off | sysctl net.ipv4.ip_forward=1 |
| DNS doesn't resolve | resolv.conf overwritten | set DNS = 1.1.1.1 in client config |
| Speed slower than provider line | MTU too high | set MTU = 1420 on Interface |
| Drops every few minutes | stateful NAT timeout | add PersistentKeepalive = 25 |
Performance reality
WireGuard adds 1-3 % CPU overhead per Gbps on a modern x86. On a 1 Gbps line, expect 920-960 Mbps real throughput; the gap is mostly UDP encapsulation overhead, not crypto.
