Step-by-step tutorial, 2026
WireGuard on your own VPS. Tested with the firewall on.
Most WireGuard guides skip the firewall. With ufw on, that’s exactly where a setup breaks. We tested each piece, so you know which one is missing.
- No handshakeUDP port closed
ufw allow 51820/udp - Handshake, no internetForwarding, NAT or ufw route missing
ufw route allow in on wg0 out on eth0 - Internet, no DNSResolver on the server blocked
ufw allow in on wg0 to any port 53
The short answer
Install wireguard, make a key pair for the server and each device, write /etc/wireguard/wg0.conf, and start it with systemctl enable --now wg-quick@wg0. For all traffic to go through it, the server also needs IP forwarding, a NAT rule, and with ufw on, ufw allow 51820/udp plus ufw route allow in on wg0 out on eth0.
Miss one of those and you get the classic “connected, but no internet”. Our builder below writes the whole setup as one script, tested end to end.
WireGuard is the easy VPN. A few lines of config, one UDP port, and your phone is talking to your server.
Then you turn on the firewall, and it stops working. Or it says connected and nothing loads.
We wanted to know exactly why. So we built a server and a client whose only way to the internet was the tunnel, switched ufw on, and added the pieces one at a time. Three separate settings each had to be there. Leave out any one of them and the handshake still works, and the internet doesn’t.
Most WireGuard guides don’t mention ufw at all, and some point the NAT rule at the wrong interface. This one is built from what we measured. Your own VPN, on your own server, in the country you pick.
In this guide you’ll learn
- A script builder: one script that sets up the server and every device
- The manual steps, so you know what each line does
- Our test: which missing piece causes which symptom
- Troubleshooting by symptom: no handshake, no internet, no DNS
- Keeping it safe: keys, client files and the firewall
- What a VPN on your server does, and what it doesn’t
Chapter 1
What you’re building
WireGuard is a VPN built into the Linux kernel. Each device has a key pair, and the server only accepts devices whose public key it knows.
| All traffic (full tunnel) | VPN network only (split) | |
|---|---|---|
| Good for | Public Wi-Fi, travel, one fixed exit IP | Reaching services on your server |
| Device setting | AllowedIPs = 0.0.0.0/0 | AllowedIPs = 10.8.0.0/24 |
| Server needs forwarding and NAT | Yes | No |
| Websites see | Your server’s IP | Your normal IP |
Not sure WireGuard is the right choice? We measured it against OpenVPN in WireGuard vs OpenVPN.
You need: an Ubuntu or Debian server with a public IPv4 address, SSH access with sudo, and the free WireGuard app on each device.
No server yet? A VPN is the classic job for a Lite 2: pick the country you want your traffic to leave from, and it bills by the hour, never more than the monthly price.
Chapter 2
Build your setup script
Fill in your server and devices. Copy the script to your server, run it once with sudo bash wg-setup.sh, and scan each QR code with the WireGuard app.
#!/bin/bash
# WireGuard server setup. Generated by litekeep.com/tutorials/wireguard-vps/
# Run once as root on a fresh Ubuntu or Debian server: sudo bash wg-setup.sh
set -euo pipefail
[ -e /etc/wireguard/wg0.conf ] && { echo "/etc/wireguard/wg0.conf already exists. Stopping so no keys are replaced."; exit 1; }
apt-get update
apt-get install -y wireguard qrencode iptables
EXT=$(ip route show default | awk '{print $5; exit}')
echo "Internet interface: $EXT"
umask 077
mkdir -p /etc/wireguard/clients
cd /etc/wireguard
wg genkey | tee server.key | wg pubkey > server.pub
cat > wg0.conf <<EOF
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = $(cat server.key)
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o $EXT -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o $EXT -j MASQUERADE
EOF
# Client: phone
wg genkey | tee clients/phone.key | wg pubkey > clients/phone.pub
cat >> wg0.conf <<EOF
[Peer]
# phone
PublicKey = $(cat clients/phone.pub)
AllowedIPs = 10.8.0.2/32
EOF
cat > /etc/wireguard/clients/phone.conf <<EOF
[Interface]
PrivateKey = $(cat clients/phone.key)
Address = 10.8.0.2/32
DNS = 1.1.1.1
[Peer]
PublicKey = $(cat server.pub)
Endpoint = 203.0.113.10:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
EOF
# Client: laptop
wg genkey | tee clients/laptop.key | wg pubkey > clients/laptop.pub
cat >> wg0.conf <<EOF
[Peer]
# laptop
PublicKey = $(cat clients/laptop.pub)
AllowedIPs = 10.8.0.3/32
EOF
cat > /etc/wireguard/clients/laptop.conf <<EOF
[Interface]
PrivateKey = $(cat clients/laptop.key)
Address = 10.8.0.3/32
DNS = 1.1.1.1
[Peer]
PublicKey = $(cat server.pub)
Endpoint = 203.0.113.10:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
EOF
# Let the server route client traffic (survives reboots)
echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-wireguard.conf
sysctl -p /etc/sysctl.d/99-wireguard.conf
# Firewall: only adds rules, never switches ufw on or off
if command -v ufw >/dev/null; then
ufw allow 51820/udp
ufw route allow in on wg0 out on "$EXT"
fi
systemctl enable --now wg-quick@wg0
wg show wg0
echo; echo "== phone: scan with the WireGuard app =="
qrencode -t ansiutf8 < /etc/wireguard/clients/phone.conf
echo; echo "== laptop: scan with the WireGuard app =="
qrencode -t ansiutf8 < /etc/wireguard/clients/laptop.conf
echo; echo "Client files are in /etc/wireguard/clients/. They contain private keys: copy them over SSH, then delete what you no longer need."It installs WireGuard, makes the keys, opens the port, sets up forwarding, starts the VPN and prints a QR code per device. It stops if a WireGuard setup already exists.
Need the server? Deploy a Lite 2 in the country you want your traffic to leave from, paste this script, and your VPN is up once the server is ready (usually within 15 minutes).
Chapter 3
The same setup, by hand
Prefer to type it yourself? This is the full-tunnel setup with one device. Run it as root.
Install, and find your internet interface
apt update && apt install -y wireguard qrencode iptables ip route show default
The word after
devis your interface: ofteneth0, sometimesens3. Use it wherever eth0 appears below.Make keys, readable by root only
cd /etc/wireguard && umask 077 wg genkey | tee server.key | wg pubkey > server.pub wg genkey | tee phone.key | wg pubkey > phone.pub
Write the server config
/etc/wireguard/wg0.conf, with the keys pasted in:[Interface] Address = 10.8.0.1/24 ListenPort = 51820 PrivateKey = <contents of server.key> PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE [Peer] PublicKey = <contents of phone.pub> AllowedIPs = 10.8.0.2/32
Turn on forwarding, for good
echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-wireguard.conf sysctl -p /etc/sysctl.d/99-wireguard.conf
Open the firewall: the port, and the route
ufw allow 51820/udp ufw route allow in on wg0 out on eth0
The first lets devices connect. The second lets their traffic through the server: ufw drops forwarded traffic by default (
DEFAULT_FORWARD_POLICY="DROP"), and the route rule makes one exception.Start it, now and at every boot
systemctl enable --now wg-quick@wg0 wg show
Make the device config
[Interface] PrivateKey = <contents of phone.key> Address = 10.8.0.2/32 DNS = 1.1.1.1 [Peer] PublicKey = <contents of server.pub> Endpoint = 203.0.113.10:51820 AllowedIPs = 0.0.0.0/0 PersistentKeepalive = 25
Save it as
phone.conf, then show it as a QR code withqrencode -t ansiutf8 < phone.confand scan it in the app.
Chapter 4
What we found: one symptom, three causes
Ubuntu 24.04, ufw on with only SSH allowed. Our test client had no way to the internet except the tunnel, so a page loading proved the tunnel carried it.
| Server state | Handshake | Internet through the tunnel |
|---|---|---|
| ufw on, only SSH allowed | No | No |
| + ufw allow 51820/udp | Yes | No |
| + ufw route allow in on wg0 out on eth0 | Yes | Yes |
| Same, but NAT rule removed | Yes | No |
| Same, but ip_forward = 0 | Yes | No |
So “connected” in the app only proves the first row is fixed. Forwarding, NAT and the ufw route rule are each needed on their own.
DNS was a separate case. Public DNS like 1.1.1.1 travelled through the tunnel fine. A resolver running on the server itself was blocked by ufw until we allowed port 53 on wg0.
Chapter 5
Troubleshooting by symptom
Start with sudo wg show on the server. A peer with a “latest handshake” line has connected at least once.
- No handshake at allHow to check: The UDP port is closed or the keys don’t match. Check
sudo ufw statusshows 51820/udp, any cloud firewall in front of the server, the Endpoint IP and port on the device, and that each side has the other side’s public key. Public and private keys look alike, and mixing them up is an easy mistake. - Handshake, but no internetHow to check:
sysctl net.ipv4.ip_forwardmust say 1.sudo iptables -t nat -S POSTROUTINGmust show the MASQUERADE rule with your real interface.sudo ufw statusmust show the ALLOW FWD line. - Internet works, names don’t loadHow to check: DNS. If the device uses your server as DNS, run
sudo ufw allow in on wg0 to any port 53. Otherwise tryDNS = 1.1.1.1in the device config. - Works, then drops after a whileHow to check: The device is behind NAT that forgets idle connections.
PersistentKeepalive = 25on the device keeps it open. - Linux client: “resolvconf: command not found”How to check: wg-quick applies the
DNS =line through resolvconf. Install a provider for it (on Debian,openresolv), or remove the DNS line.
Chapter 6
Keeping it safe
A VPN is a door into your server. A few habits keep it yours.
- Private keys stay private. The
umask 077makes every key and config readable by root only. The client files contain the device’s private key: copy them over SSH or show the QR code, then delete them. - One key per device. Each device gets its own /32 address. Lose a phone? Delete its
[Peer]block and restart; nothing else changes. - The tunnel works both ways. Once connected, devices on the VPN can connect back to you. Your devices can reach each other unless you add firewall rules for wg0.
- Want an extra layer? A PresharedKey per peer adds symmetric encryption on top. Both sides need the same one.
MythA VPN on my server makes me anonymous.
It protects your traffic on untrusted networks and shows websites your server’s IP instead of yours. That’s privacy from the network you’re on, not anonymity.
MythIf the app says connected, everything goes through the tunnel.
Only what AllowedIPs covers. Our script is IPv4 only, so a device with IPv6 may still send IPv6 traffic outside the tunnel.
Chapter 7
Choosing where your VPN lives
WireGuard is light. What changes how it feels is how far your server is from you.
Every request travels to your server and back before it goes anywhere else. Pick a location close to where you’ll use it, or in the country you want websites to see. LiteKeep runs in 15 countries at one price, and each one on our locations page shows the fastest a round trip from major cities could ever be.
Now it’s your move
A working WireGuard server is a key per device, one config file and three server settings. Now you know all three.
Next, lock down SSH with our SSH hardening tutorial, and make sure updates install themselves with the unattended upgrades guide.
Ready to connect? Deploy a Lite 2, paste the script from the builder, and scan the QR code. Your server is usually ready within 15 minutes, and the VPN is up right after.