A self-hosted WireGuard VPN on a $5/month VPS gives you a private exit IP you fully control — no logs but your own, no shared IP pools, no monthly per-device fees. Here's a clean, copy-pasteable quickstart on Ubuntu. (For DNS hardening, a kill switch and multi-client management, see the full tutorial linked at the end.)
Prerequisites
- A VPS with a public IPv4 (Contabo, Hetzner, OVH… any works), Ubuntu 22.04+ with root.
-
UDP 51820reachable (open it in the provider firewall).
1. Install WireGuard
sudo apt update && sudo apt install -y wireguard
On Linux 5.6+ WireGuard is a kernel module — minimal overhead, no userspace daemon.
2. Generate server keys
cd /etc/wireguard
umask 077
wg genkey | tee server.key | wg pubkey > server.pub
umask 077 matters: the private key must not be world-readable.
3. Server config — /etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.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
Replace eth0 with your real public interface (ip route get 1.1.1.1 shows it).
4. Enable IP forwarding
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wg.conf
sudo sysctl --system
5. Bring it up (and on boot)
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0
sudo wg show should now list the interface with its public key and listening port.
6. Add a client
Generate a keypair on the client (or server-side), then add a peer to wg0.conf:
[Peer]
PublicKey = <client public key>
AllowedIPs = 10.8.0.2/32
Reload without dropping the tunnel:
sudo wg syncconf wg0 <(wg-quick strip wg0)
Client config (wg0.conf on the device):
[Interface]
PrivateKey = <client private key>
Address = 10.8.0.2/24
DNS = 1.1.1.1
[Peer]
PublicKey = <server public key>
Endpoint = <server public IP>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
AllowedIPs = 0.0.0.0/0 routes all traffic through the tunnel. PersistentKeepalive = 25 keeps NAT mappings alive on mobile networks.
7. Verify
# on the client, after wg-quick up:
curl -s https://ifconfig.me # should return the VPS IP
Also run a DNS-leak check in the browser — if your real resolver shows up, set DNS = in the client [Interface] (as above) and confirm.
Common gotchas
-
No traffic flows → wrong
eth0name inPostUp, orip_forwardnot applied. -
Works on Wi-Fi, drops on 4G → add
PersistentKeepalive = 25(above). - Handshake but no internet → MASQUERADE rule missing or wrong outbound interface.
Where to go next
This is the minimal working setup. For provider selection (price/jurisdiction), DNS hardening, a Linux kill switch with iptables, and managing several clients, here's a complete step-by-step guide:
Top comments (0)