Set up a WireGuard VPN on your VPS in 10 minutes
A working WireGuard VPN with one peer config you can scan into your phone. No third-party clients, no telemetry, no monthly bill.

WireGuard is the smallest, fastest VPN protocol that actually feels modern. Setup is two config files and a systemd unit. There is no reason to pay a commercial VPN provider when a small VPS gives you a faster, more honest tunnel.
Install
sudo apt update
sudo apt install -y wireguard qrencode
The qrencode package is for generating phone-scannable QR codes later.
Generate keys
Server keys. The /etc/wireguard directory is root-owned, so a plain cd into it fails as a regular user; write the files through sudo tee with full paths instead:
wg genkey | sudo tee /etc/wireguard/server.key | wg pubkey | sudo tee /etc/wireguard/server.pub
sudo chmod 600 /etc/wireguard/server.key
Client keys (do this for each peer). Generate these in your home directory as your regular user; the same permission problem means they cannot be written into /etc/wireguard without sudo:
cd ~
wg genkey | tee client1.key | wg pubkey > client1.pub
chmod 600 client1.key
Server config
Create /etc/wireguard/wg0.conf. The directory is root-only, so a plain editor can't save there; open the file with sudoedit (or sudo nano if you prefer):
sudoedit /etc/wireguard/wg0.conf
Paste in the following:
[Interface]
Address = 10.66.66.1/24, fd42:42::1/64
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY_HERE
PostUp = iptables -A FORWARD -i %i -j ACCEPT
PostUp = iptables -A FORWARD -o %i -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostUp = ip6tables -A FORWARD -i %i -j ACCEPT
PostUp = ip6tables -A FORWARD -o %i -j ACCEPT
PostUp = ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT
PostDown = iptables -D FORWARD -o %i -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
PostDown = ip6tables -D FORWARD -i %i -j ACCEPT
PostDown = ip6tables -D FORWARD -o %i -j ACCEPT
PostDown = ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = CLIENT1_PUBLIC_KEY_HERE
AllowedIPs = 10.66.66.2/32, fd42:42::2/128
Replace the placeholders with the keys you generated earlier. The server's private key is readable only by root, so print both values like this and paste them in:
sudo cat /etc/wireguard/server.key # goes in PrivateKey
cat ~/client1.pub # goes in the peer's PublicKey
Confirm eth0 is your real outbound interface (ip route get 1.1.1.1).
One check before you go further: the IPv6 half of this config only works if your VPS has a public IPv6 address on eth0. Run ip -6 addr show eth0 and look for a scope global address. The ip6tables MASQUERADE rule NATs the tunnel's private fd42:42:: range out through that address. If your VPS is IPv4-only, drop the fd42:42:: addresses from this config and the client config, remove the ip6tables lines, and set the client's AllowedIPs to 0.0.0.0/0 only. Skip that cleanup and the client will route all its IPv6 traffic into a tunnel that cannot deliver it, so every IPv6 destination goes dark while connected.
Enable IP forwarding
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wg.conf
echo 'net.ipv6.conf.all.forwarding=1' | sudo tee -a /etc/sysctl.d/99-wg.conf
sudo sysctl --system
Start it
sudo systemctl enable --now wg-quick@wg0
sudo wg show
You should see your peer listed with its allowed IPs. The latest handshake line only appears after the client connects for the first time, so don't worry that it's missing right now.
Open the firewall
First check which firewall you're actually running with sudo ufw status. If it prints Status: active, allow the WireGuard port:
sudo ufw allow 51820/udp
If that status command reports inactive, or ufw isn't installed at all, the rule above does nothing for you. UDP 51820 still has to be permitted in whatever firewall does apply: host-level nftables or iptables rules, or your VPS provider's cloud firewall or security-group panel. A blocked port fails silently here, the handshake just never completes, so sort this out now rather than blaming your configs later.
Client config
Create client1.conf in your home directory (no sudo needed for this one):
[Interface]
PrivateKey = CLIENT1_PRIVATE_KEY_HERE
Address = 10.66.66.2/24, fd42:42::2/64
DNS = 1.1.1.1, 1.0.0.1
[Peer]
PublicKey = SERVER_PUBLIC_KEY_HERE
Endpoint = your.vps.ip:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
Replace the two placeholders here as well. The client's private key sits in your home directory; the server's public key is under /etc/wireguard, so it needs sudo to read:
cat ~/client1.key # goes in PrivateKey
sudo cat /etc/wireguard/server.pub # goes in the peer's PublicKey
Also swap your.vps.ip in Endpoint for your server's actual public IP address.
AllowedIPs = 0.0.0.0/0, ::/0 routes everything through the tunnel. If you went IPv4-only on the server earlier, drop fd42:42::2/64 from Address and set AllowedIPs = 0.0.0.0/0 here to match.
Phone scan it as a QR:
qrencode -t ansiutf8 < client1.conf
Open the WireGuard mobile app, "Add tunnel from QR code", point camera at terminal. Connected.
Verify
From the client, hit https://ifconfig.co. You should see your VPS IP. Run a leak test at dnsleaktest.com to confirm DNS goes via the tunnel.
A few gotchas
- MTU: if connections feel flaky on cell networks, try
MTU = 1420in the[Interface]block on the client. - NAT: if you're behind aggressive carrier NAT, the
PersistentKeepalive = 25keeps the session alive. - DNS leaks: if your client OS overrides DNS, force it with
wg-quick'sDNS =line andresolvconf.
That's it. Your own VPN. No subscription, no telemetry, no provider mining your DNS.
Continue reading