A personal WireGuard server costs about five dollars a month, takes twenty minutes to build, and removes the question of whether a provider is really keeping its promises — because you are the provider.
It also changes the threat model rather than eliminating it: your traffic now exits from an IP address that belongs to you alone, which is excellent for privacy from your ISP and poor for blending into a crowd. Decide which you want before you start.
What you need
- A VPS with a public IPv4 address. Any provider with a clear acceptable-use policy will do; 1 vCPU and 512 MB RAM is plenty.
- Debian 12 or Ubuntu 24.04.
- SSH access with a key, not a password.
- Twenty minutes.
1. Update and install
sudo apt update && sudo apt upgrade -y
sudo apt install wireguard wireguard-tools -y2. Generate the server keys
Keys are just files. Treat the private key like a password — it never leaves the server.
umask 077
wg genkey | sudo tee /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key3. Enable forwarding
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
echo "net.ipv6.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p4. Write the server config
Replace SERVER_PRIVATE_KEY with the contents of the private key file, and eth0 with your actual interface name (check with ip route).
sudo nano /etc/wireguard/wg0.conf
[Interface]
Address = 10.66.66.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 MASQUERADE5. Create a client
Generate a separate key pair for every device. Never reuse one profile across a laptop and a phone — if one is lost you want to revoke only that device.
wg genkey | tee client_private.key | wg pubkey > client_public.keyAdd the client to the server config:
[Peer]
PublicKey = CLIENT_PUBLIC_KEY
AllowedIPs = 10.66.66.2/32And the client profile, on your device:
[Interface]
PrivateKey = CLIENT_PRIVATE_KEY
Address = 10.66.66.2/24
DNS = 9.9.9.9
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = YOUR_SERVER_IP:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 256. Start it and make it permanent
sudo systemctl enable --now wg-quick@wg0
sudo wg showThe parts most tutorials leave out
DNS is where self-hosted VPNs leak
If you do not set DNS = in the client profile, your device keeps using whatever resolver it had — frequently your ISP’s, which defeats a large part of the point. Set it explicitly, then verify with a leak test rather than assuming.
IPv6 will bypass an IPv4-only tunnel
If your device has IPv6 connectivity and your tunnel does not carry it, IPv6 traffic goes straight out unencrypted. Either carry IPv6 in the tunnel (add an IPv6 address and route) or disable IPv6 on the client. Half-measures leak.
Lock the server down
- Firewall: allow only 22/tcp (ideally from your IP) and 51820/udp.
- Disable password authentication in
/etc/ssh/sshd_config. - Enable unattended security upgrades — an unpatched personal VPN is worse than a commercial one.
- Do not run anything else on this box. It is a router, not a web server.
Key rotation and revocation
Revoking a device means deleting its [Peer] block and reloading: sudo systemctl reload wg-quick@wg0. Rotate the server key if you ever suspect the host was compromised — every client profile must then be reissued, which is a good reason to keep them documented.
When self-hosting is the wrong answer
You get one exit IP, tied to your account with the hosting company, used by nobody else. For privacy from your ISP, from public wifi, and for reaching your own network, that is ideal. For blending into a large pool of users, or for anything where the hosting provider’s records are part of your threat model, a reputable commercial provider is genuinely better.
Both are legitimate. Pick the one that matches the threat you actually have — see our threat modelling guide if you are not sure which that is.
