Skip to content
Launch offerNew accounts: we double your first deposit of $100 or more, up to $250 bonus.Claim my bonus
LiteKeep
Sign inDeploy a server

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.

  • 14 min read
  • 7 chapters
  • Updated
  1. No handshakeUDP port closedufw allow 51820/udp
  2. Handshake, no internetForwarding, NAT or ufw route missingufw route allow in on wg0 out on eth0
  3. Internet, no DNSResolver on the server blockedufw 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.

Two ways to use it
All traffic (full tunnel)VPN network only (split)
Good forPublic Wi-Fi, travel, one fixed exit IPReaching services on your server
Device settingAllowedIPs = 0.0.0.0/0AllowedIPs = 10.8.0.0/24
Server needs forwarding and NATYesNo
Websites seeYour server’s IPYour 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.

Your VPN

Nothing you type leaves this page. Keys are created on your server.

wg-setup.sh: run once with sudo bash wg-setup.sh
#!/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.

  1. Install, and find your internet interface

    apt update && apt install -y wireguard qrencode iptables
    ip route show default

    The word after dev is your interface: often eth0, sometimes ens3. Use it wherever eth0 appears below.

  2. 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
  3. 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
  4. 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
  5. 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.

  6. Start it, now and at every boot

    systemctl enable --now wg-quick@wg0
    wg show
  7. 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 with qrencode -t ansiutf8 < phone.conf and 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.

Our test, October 2026: adding one piece at a time
Server stateHandshakeInternet through the tunnel
ufw on, only SSH allowedNoNo
+ ufw allow 51820/udpYesNo
+ ufw route allow in on wg0 out on eth0YesYes
Same, but NAT rule removedYesNo
Same, but ip_forward = 0YesNo

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.

  1. No handshake at allHow to check: The UDP port is closed or the keys don’t match. Check sudo ufw status shows 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.
  2. Handshake, but no internetHow to check: sysctl net.ipv4.ip_forward must say 1. sudo iptables -t nat -S POSTROUTING must show the MASQUERADE rule with your real interface. sudo ufw status must show the ALLOW FWD line.
  3. 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 try DNS = 1.1.1.1 in the device config.
  4. Works, then drops after a whileHow to check: The device is behind NAT that forgets idle connections. PersistentKeepalive = 25 on the device keeps it open.
  5. 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 077 makes 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.

Fact

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.

Fact

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.