Best VPS for a WireGuard VPN Server, and How to Set It Up
What a WireGuard VPN server really needs from a VPS (traffic, location and a clean IP, not a big CPU), how the cheap providers compare, and a tested script that sets up the server and adds each phone with a QR code.
A WireGuard VPN server is light. It runs inside the Linux kernel and is happy on the smallest VPS you can rent. That is why the “best VPS for a VPN” is rarely the one with the most CPU: what decides how good your VPN feels is how much traffic the plan includes, where the server is, and how clean its IP address is.
This guide shows what to look for, compares the cheap providers, and then gives you two tested scripts: one that sets up the WireGuard server, and one that adds a phone or laptop and shows a QR code to scan.
What a VPN server needs from a VPS
| Need | Why it matters | Enough for a personal VPN |
|---|---|---|
| Monthly traffic | Everything your devices download passes through the server and leaves it again. Video uses 1 to 3 GB an hour. | 500 GB to 1 TB a month |
| Location | A server near you is faster. A server in another country gives you that country's IP address. | The country you want to appear in |
| A clean IPv4 address | Some cloud IP ranges get extra captchas or are blocked by streaming sites. | A normal IPv4 address (not IPv6 only) |
| UDP and a firewall you control | WireGuard uses one UDP port. You must be able to open it. | UDP 51820 open |
| CPU and memory | WireGuard is very efficient; a small server reaches hundreds of megabits. | 1 vCPU, 512 MB to 1 GB RAM |
| Terms of service | A private VPN is allowed almost everywhere. A public VPN for app users brings abuse reports (torrents, copyright notices), and some providers suspend for that. | Read the acceptable use policy |
Cheap VPS options compared
These are the usual choices for a WireGuard server. There are no affiliate links on this page.
| Provider | Cheapest useful plan | Traffic included | Good to know |
|---|---|---|---|
| Oracle Cloud Free Tier | Free: an Arm server with up to 2 cores and 12 GB, or two small AMD servers | 10 TB a month | Free for good, but servers are often “out of capacity” and the setup has a firewall trap. See our Oracle Cloud free VPS guide. |
| Hetzner Cloud (CX23) | About €4 to €6 a month (prices rose twice in 2026) | 20 TB in Europe, about 1 TB in the US | The most traffic for the money in Europe. Strict about abuse reports. |
| DigitalOcean | $4 a month, 512 MB | 500 GB | Simple panel, many locations |
| Vultr | $5 a month, 1 GB | 1 TB | Many countries. The $2.50 plan is IPv6 only, which is not enough for a VPN. |
| Akamai (Linode) Nanode | $5 a month, 1 GB | 1 TB | Reliable network, simple pricing |
Prices as listed in October 2026; check the provider's own page before you buy. For yourself and your family, the free Oracle server or any $4 to $5 plan is plenty. For a VPN app with many users, traffic is the real cost: pick plans with many terabytes included, and put servers in several countries.
Set up the server with one script
- Create a VPS with Ubuntu 24.04 (Ubuntu 22.04 and Debian 12 work too).
- In the provider's control panel, open UDP port 51820 in its firewall, if it has one.
- Log in with SSH, save the script below as
wg-setup.sh(for example withnano wg-setup.sh), and runsudo bash wg-setup.sh.
#!/bin/bash
# WireGuard VPN server for Ubuntu 22.04 / 24.04 or Debian 12.
# Run once as root: sudo bash wg-setup.sh
set -euo pipefail
PORT=51820 # the UDP port phones connect to
NET=10.8.0 # tunnel addresses: the server is .1, devices get .2, .3 ...
DNS="1.1.1.1, 1.0.0.1" # DNS servers the devices use inside the tunnel
[ "$(id -u)" -eq 0 ] || { echo "Run it as root: sudo bash $0"; exit 1; }
[ -f /etc/wireguard/wg0.conf ] && { echo "/etc/wireguard/wg0.conf already exists. Add devices with wg-add-client.sh"; exit 1; }
apt-get update
apt-get install -y wireguard qrencode iptables curl
# The network card that leads to the internet (eth0, ens3, enp0s6 ...)
WAN=$(ip -4 route show default | awk '{print $5; exit}')
# The public IP, asked from outside: on Oracle, AWS and Google Cloud it is not on the network card
IP=$(curl -4 -fsS https://api.ipify.org) || { echo "Could not read the public IP. Check the internet connection."; exit 1; }
mkdir -p /etc/wireguard/clients
chmod 700 /etc/wireguard
cd /etc/wireguard
umask 077
wg genkey > server.key
wg pubkey < server.key > server.pub
# Rules are inserted at the top (-I), so a REJECT rule further down cannot block them
cat > wg0.conf <<EOF
[Interface]
Address = $NET.1/24
ListenPort = $PORT
PrivateKey = $(cat server.key)
PostUp = iptables -I INPUT 1 -p udp --dport $PORT -j ACCEPT; iptables -I FORWARD 1 -i wg0 -j ACCEPT; iptables -I FORWARD 1 -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -s $NET.0/24 -o $WAN -j MASQUERADE
PostDown = iptables -D INPUT -p udp --dport $PORT -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -s $NET.0/24 -o $WAN -j MASQUERADE
EOF
# Values the add-client script reads
printf 'IP=%s\nPORT=%s\nNET=%s\nDNS="%s"\n' "$IP" "$PORT" "$NET" "$DNS" > server.env
# Let the server pass traffic from the tunnel to the internet
echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-wireguard.conf
sysctl --system > /dev/null
if command -v ufw > /dev/null && ufw status | grep -q 'Status: active'; then
ufw allow "$PORT/udp"
fi
systemctl enable --now wg-quick@wg0
echo "WireGuard is running on $IP, UDP port $PORT (network card $WAN)."
echo "Add a device: sudo bash wg-add-client.sh phone1"
| Part | What it does |
|---|---|
ip -4 route show default | Finds the network card that leads to the internet. It is not always eth0: Oracle uses names like enp0s6, others ens3. |
curl ... api.ipify.org | Reads the public IP from outside. On Oracle, AWS and Google Cloud the public IP is not on the network card, so reading it locally gives the wrong address. |
wg genkey, wg pubkey | The server's private key stays on the server; the public key goes into every device's file. |
iptables -I ... 1 | The rules are inserted at the top. Some images (Oracle's Ubuntu, for one) end their rules with a REJECT; rules added at the end would never be reached. |
MASQUERADE | Devices' traffic leaves the server with the server's IP, which is what makes it a VPN. |
net.ipv4.ip_forward = 1 | Lets Linux pass traffic from the tunnel to the internet. Without it, devices connect but nothing loads. |
server.env | Saves the IP, port and DNS for the second script. |
Add a phone with a QR code
Save this as wg-add-client.sh next to the first script. Run it once per device with a name, for example sudo bash wg-add-client.sh phone1:
#!/bin/bash
# Add a phone or computer to the WireGuard server.
# Usage: sudo bash wg-add-client.sh phone1
set -euo pipefail
NAME=${1:-}
[[ $NAME =~ ^[A-Za-z0-9_-]+$ ]] || { echo "Give a name of letters, numbers, - or _ : sudo bash $0 phone1"; exit 1; }
cd /etc/wireguard
source ./server.env
umask 077
[ -f "clients/$NAME.conf" ] && { echo "clients/$NAME.conf already exists"; exit 1; }
# The first free address: .2, .3 ... (.1 is the server)
N=""
for i in $(seq 2 254); do
grep -q "AllowedIPs = $NET.$i/32" wg0.conf || { N=$i; break; }
done
[ -n "$N" ] || { echo "No free addresses left in $NET.0/24"; exit 1; }
KEY=$(wg genkey)
PUB=$(echo "$KEY" | wg pubkey)
PSK=$(wg genpsk)
# Add the device to the server. It is saved in wg0.conf, so it survives a restart.
cat >> wg0.conf <<EOF
[Peer]
# $NAME
PublicKey = $PUB
PresharedKey = $PSK
AllowedIPs = $NET.$N/32
EOF
wg syncconf wg0 <(wg-quick strip wg0)
# The device's own config file
cat > "clients/$NAME.conf" <<EOF
[Interface]
PrivateKey = $KEY
Address = $NET.$N/32
DNS = $DNS
MTU = 1280
[Peer]
PublicKey = $(cat server.pub)
PresharedKey = $PSK
Endpoint = $IP:$PORT
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
EOF
qrencode -t ansiutf8 < "clients/$NAME.conf"
echo "Saved /etc/wireguard/clients/$NAME.conf ($NET.$N). Scan the code above in the WireGuard app."
On the phone, install the official WireGuard app (Android or iPhone), tap +, choose Scan from QR code and point the camera at the code in your terminal. On a computer, copy the file from /etc/wireguard/clients/ and import it in the WireGuard app.
| Part | What it does |
|---|---|
The loop over .2 to .254 | Gives each device the first free address, so every device has its own |
wg genpsk | A preshared key: an extra secret on top of the key pair, for each device |
wg syncconf | Adds the device to the running server without cutting the others off |
MTU = 1280 | Mobile networks often drop large packets; a smaller MTU avoids “connected but no internet” |
PersistentKeepalive = 25 | Keeps the connection alive behind mobile and home routers |
qrencode -t ansiutf8 | Draws the device's file as a QR code right in the terminal |
Check that it works
Turn the VPN on in the app and open any “what is my IP” page: it should show your server's IP. On the server:
# On the server: every device, its last handshake and how much it has used
sudo wg show
# The server's public IP, to compare with what the phone shows
curl -4 https://api.ipify.org
A recent latest handshake and growing transfer numbers mean the device is connected and using the tunnel. To take a device off the server:
# Remove a device: its public key is in /etc/wireguard/wg0.conf, under its name
sudo wg set wg0 peer 'THE-DEVICE-PUBLIC-KEY=' remove
# Then delete its [Peer] block from wg0.conf and its file in /etc/wireguard/clients/
Common problems
| Problem | Cause and fix |
|---|---|
No handshake in wg show | UDP 51820 is closed in the provider's firewall (the panel, not the server), or the device's file has the wrong IP. On Oracle, also check the security list. |
| Connected, but nothing loads | Forwarding is off, or the network card name is wrong. Check sysctl net.ipv4.ip_forward and the -o name in wg0.conf. |
| Works on Wi-Fi, not on mobile data | Usually the MTU (it is already 1280 here). Some mobile and office networks block UDP: then you need OpenVPN on TCP 443. |
| Slow speed | The server is far from you, or the cheap plan shares its CPU heavily. Try a server in a nearer location. |
| Streaming sites block you | That data centre's IP range is known as a server range. Try another provider or location. |
| Nothing works after a reboot | Check systemctl status wg-quick@wg0; the script enables it, so it should start by itself. |
From one server to a VPN app with many users
The scripts above are right for you and your family, where you scan each QR code yourself. A VPN app that strangers download from Google Play needs a different setup:
- Devices register themselves. Each phone makes its own key pair and sends only the public key to your server, which hands out an address and adds it automatically. Nobody scans anything.
- An IP pool bigger than 254 devices. One
/24network runs out fast, so you need several pools or a larger range, and a way to free addresses of devices that never come back. - A server list. The app downloads a list of servers in several countries from your website, so you add a server without updating the app.
- Traffic and abuse. You watch traffic per server, spread users over servers, and deal with abuse reports before the provider suspends you.
Building and testing that part takes weeks.
The finished version: our VPN app source code
Our Android VPN app source code is that many-user setup, finished:
- Kotlin and Jetpack Compose, with WireGuard and OpenVPN servers in one list and one-tap connect
- A key pair made on each phone, and a registration script that gives each device an address from your IP pool
- A server script that adds the registered devices to WireGuard automatically
- The server list loaded from your own website, so new servers need no app update
- Free VPN Gate OpenVPN servers with an update script, for a VPN that works before you rent any server
- A speed test, AdMob and Firebase, ready to reskin and publish
If you would rather use SSTP, the secure VPN proxy app connects to SSTP servers listed in a JSON file on your website.
Where to go next
- How to make a VPN app in Android Studio, with the whole app in MainActivity
- Oracle Cloud free tier VPS: run this server for free
- Free SSL on Ubuntu, for the HTTPS registration page of a VPN app
- What actually matters in cheap web hosting