I don't use commercial VPNs. The threat model they advertise — protecting you from your ISP — is real, but the threat model they introduce — trusting some third-party VPN provider with all your traffic — is worse. Self-hosted is the only honest answer. WireGuard is the only honest implementation.
Why WireGuard, not OpenVPN
OpenVPN works. It's also a TLS-based protocol with all the complexity that implies, ~70k lines of code in the reference implementation, and a configuration surface that has historically been a footgun for self-hosters. WireGuard is ~4k lines, lives in the kernel on Linux, and the entire config for a working server is shorter than this paragraph.
# /etc/wireguard/wg0.conf — server
[Interface]
PrivateKey = <server priv key>
Address = 10.6.0.1/24
ListenPort = 51820
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] # my phone
PublicKey = <phone pub key>
AllowedIPs = 10.6.0.2/32
[Peer] # my laptop
PublicKey = <laptop pub key>
AllowedIPs = 10.6.0.3/32
That's the whole server. The client config is similarly tiny. pivpn add generates the QR code for the phone. Scan, connect, done.
What I actually use it for
Three things, in order of frequency:
[ DAILY USE ] 1. on coffee shop wifi → tunnel everything home, AdGuard-filter 2. accessing home SFTP → SFTP server only listens on LAN; VPN is the LAN 3. checking on home iot → camera feed, light controllers, etc.
Note what isn't on the list: streaming geo-restricted content. That's what people associate with VPNs. I run mine for the network-security and home-LAN-access reasons; my UK exit IP doesn't help me with US Netflix.
The DNS leak fix
Default WireGuard configs leak DNS. The tunnel routes traffic via the home server, but the OS keeps using the cafe Wi-Fi's DNS resolver. Cafe owner sees every domain you query, even though the actual fetches go through the tunnel. Fix:
# client config — DNS = the home AdGuard
[Interface]
PrivateKey = <client priv key>
Address = 10.6.0.2/32
DNS = 10.6.0.1 # <-- this is the fix
[Peer]
PublicKey = <server pub key>
Endpoint = home.example.com:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Routing DNS through the home AdGuard means my phone gets the same blocklist applied while I'm off-network. Free win — no extra config beyond the line above.
Dynamic DNS for the home endpoint
Most home ISPs hand out dynamic IPs. I use Cloudflare DNS with a small cron-driven updater script that pushes the current public IP to a subdomain every 5 minutes. The WireGuard client config points to home.example.com:51820 instead of a hardcoded IP. When the IP changes, the resolver catches up before the next handshake.
Performance
The Pi 4 handles WireGuard at hundreds of megabits with the kernel module. The bottleneck on my home connection is upload bandwidth (40 Mbps), not the Pi. Battery impact on phones is negligible — WireGuard idle is genuinely idle, no constant tunnel keepalives.
Self-hosted VPN went from "weekend project I should do someday" to "thing I would notice in five minutes if it broke." That's the test for whether a homelab project is real.