RouteHardenHire us
← Back to blog
Self-Hosted Privacy··6 min read

Self-hosted WireGuard on a $5 VPS in 2026

End-to-end setup with hardened sysctl, multi-client config, DNS hygiene, and the $5 VPS providers actually worth using in 2026.

A self-hosted WireGuard server is one of the highest-leverage privacy moves you can make. For roughly $5/month you get a private endpoint with no logs but yours, no shared IP reputation, and no tunnel that quietly upsells you to a five-year plan.

This is the setup we run ourselves. It's been stable for years, takes about 20 minutes, and works on Ubuntu 24.04 LTS.

What you actually need

  • A VPS with a public IPv4 (~$5/month) — providers below
  • Root or sudo on the VPS
  • A control machine (your laptop) with ssh
  • 20 minutes

You do not need a domain, dynamic DNS, complex routing, or any "VPN management UI" software. WireGuard is fast because it's small. We're going to keep it small.

Picking a VPS

Three categories matter for a privacy-oriented WireGuard endpoint: jurisdiction, bandwidth, and IP reputation. If your VPS provider's IP block is on every reputable streaming-service blocklist, you're going to have a bad time.

Reasonable picks in 2026:

  • Hetzner — €4-5/month CX22 in Helsinki or Falkenstein. Excellent bandwidth, EU jurisdiction, clean IP space. Best general pick.
  • BuyVM — $3.50/month KVM slices in NY/LV/LU. Anti-DMCA Luxembourg location is famous; clean IPs.
  • Vultr — wide region selection if you want a specific country. Slightly higher prices, IPs occasionally flagged.
  • OVH / Kimsufi — French jurisdiction, strong privacy law, decent prices.

Avoid the cheapest "datacenter X" budget hosts — their IP space is usually so abused by spammers that streaming services and CAPTCHA providers will block you reflexively.

Server setup

SSH in as root and update first:

apt update && apt upgrade -y
apt install -y wireguard ufw fail2ban

Create a non-root user (skip if you already have one):

adduser admin
usermod -aG sudo admin
mkdir -p /home/admin/.ssh
cp ~/.ssh/authorized_keys /home/admin/.ssh/
chown -R admin:admin /home/admin/.ssh
chmod 700 /home/admin/.ssh

Lock down SSH in /etc/ssh/sshd_config.d/00-hardening.conf:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM yes

Then systemctl restart ssh. From now on, log in as admin@<server-ip>.

Generate keys

WireGuard keys are 32-byte Curve25519 keypairs. One pair per peer (server + each client). On the server:

cd /etc/wireguard
umask 077
wg genkey | tee server.key | wg pubkey > server.pub
wg genkey | tee client1.key | wg pubkey > client1.pub

The umask 077 ensures the keys are only readable by root. Never expose the .key files — only the .pub files travel to the other peer.

Server config

Create /etc/wireguard/wg0.conf:

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of server.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 MASQUERADE

[Peer]
# client1
PublicKey = <contents of client1.pub>
AllowedIPs = 10.8.0.2/32

Replace eth0 with your server's actual outbound interface (ip route | grep default to confirm). The MASQUERADE rule is what makes this server function as a NAT gateway.

Enable IP forwarding

WireGuard tunnels are useless without forwarding. Edit /etc/sysctl.d/99-wireguard.conf:

net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

# Bonus hardening
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1

Apply: sysctl -p /etc/sysctl.d/99-wireguard.conf.

Firewall

ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 51820/udp
ufw enable

That's the entire firewall. SSH and WireGuard, nothing else.

Start the tunnel

systemctl enable --now wg-quick@wg0
wg show

You should see your interface up with no peer activity yet. Time to connect a client.

Client config

On your laptop, install WireGuard (Mac: brew install wireguard-tools; Windows: official installer; iOS/Android: app store). Then create a client config — call it routeharden.conf:

[Interface]
PrivateKey = <contents of client1.key>
Address = 10.8.0.2/32
DNS = 1.1.1.1, 1.0.0.1

[Peer]
PublicKey = <contents of server.pub>
Endpoint = <your-server-ip>:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

AllowedIPs = 0.0.0.0/0, ::/0 means "send all traffic through the tunnel." Use a narrower range if you only want split-tunnel.

PersistentKeepalive = 25 is critical for clients behind NAT — without it, the NAT table on the client's router will time out the UDP connection and incoming traffic will fail.

Import the config into the WireGuard app and toggle on. You should see traffic in wg show on the server.

DNS hygiene

Setting DNS = 1.1.1.1 in the client config is necessary but not sufficient. Most DNS leaks come from two places:

  1. System resolver caching — your OS may cache the previous DNS server. Reboot or flush after first connect.
  2. Browser-level DNS-over-HTTPS — Firefox and Chrome will sometimes use their own DoH endpoint that bypasses the tunnel. Disable Firefox's "Enable DNS over HTTPS" or set Chrome's "Use secure DNS" to "with my current service provider."

Test for leaks at dnsleaktest.com. If you see your own ISP's DNS, fix one of the above.

Verifying it works

From the client:

curl -4 https://ifconfig.io       # Should return server IP
curl -6 https://ifconfig.io       # Should fail (no IPv6) or return server v6

If the IPv4 line shows your server's IP, the tunnel is up and traffic is routing through it.

What we skipped (and why)

  • WireGuard UI dashboards (wg-easy, etc.) — fine for hobbyist setups, but they add an attack surface for what's already a dead-simple text file. Skip.
  • Obfuscation / Reality / sing-box wrappers — needed only if WireGuard itself is being actively blocked at the network level. If you're in a permissive jurisdiction, raw WireGuard is enough. We'll cover obfuscated transports in a separate guide.
  • Multi-hop / cascaded VPN — three-letter-agency threat models only. Slows you down and complicates routing. Not worth it for general privacy.

Maintenance

Once a month:

apt update && apt upgrade -y
systemctl restart wg-quick@wg0

Watch wg show once in a while — if no handshake in the last 3 minutes, the client is probably offline or behind aggressive NAT.

That's it. Twenty minutes, $5/month, your own private endpoint with logs no one else can read.