Step-by-Step Guide to Deploying a WireGuard VPN Server on a Budget VPS

Step-by-Step Guide to Deploying a WireGuard VPN Server on a Budget VPS

We earn commissions when you shop through the links on this page, at no additional cost to you. Learn more.

WireGuard is, in my opinion, the best VPN protocol available right now — it's fast, lean, uses modern cryptography, and the entire codebase is small enough that security researchers can actually audit it. I've run WireGuard on everything from a $4/month RackNerd VPS to a DigitalOcean Droplet, and it genuinely works beautifully on all of them. In this guide I'll walk you through a complete, production-ready WireGuard server setup on a budget VPS running Ubuntu 22.04, including firewall rules, key generation, client configuration, and a few gotchas I've hit personally.

Choosing Your VPS

For a WireGuard server you don't need much compute power — the encryption is handled in the kernel and the overhead is minimal. I typically run mine on the smallest Droplet tier available. Create your DigitalOcean account today — the $6/month basic Droplet (1 vCPU, 1 GB RAM, 25 GB SSD) handles dozens of VPN peers without breaking a sweat. You want a VPS with a static public IP, at least 512 MB of RAM, and ideally a location close to you or wherever you need to appear to be connecting from.

Get dependable uptime with our 99.99% SLA, simple security tools, and predictable monthly pricing with DigitalOcean's virtual machines, called Droplets.

Once you've spun up your VPS, SSH in as root or a sudo user and make sure the system is up to date before we touch anything else.

Initial System Prep

The first thing I always do on a fresh VPS is update packages and enable IP forwarding. WireGuard needs IP forwarding enabled at the kernel level to route traffic from your clients out to the internet — without it, your VPN will connect but no traffic will actually flow.

# Update the system
apt update && apt upgrade -y

# Install WireGuard
apt install -y wireguard wireguard-tools

# Enable IP forwarding permanently
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
echo "net.ipv6.conf.all.forwarding = 1" >> /etc/sysctl.conf
sysctl -p

# Confirm forwarding is active
sysctl net.ipv4.ip_forward

You should see net.ipv4.ip_forward = 1 come back. If it shows 0, the sysctl change didn't apply — try rebooting and running sysctl -p again.

Generating Server Keys

WireGuard uses public/private key pairs for authentication — no passwords, no certificates, just clean Curve25519 keypairs. I always generate these with strict permissions so the private key is never world-readable.

# Create the WireGuard config directory with correct permissions
install -d -m 700 /etc/wireguard

# Generate server private and public keys
wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key

# Secure the private key
chmod 600 /etc/wireguard/server_private.key

# Print out the keys — you'll need these in the config
echo "Private key: $(cat /etc/wireguard/server_private.key)"
echo "Public key:  $(cat /etc/wireguard/server_public.key)"

# Generate a client keypair while you're at it
wg genkey | tee /etc/wireguard/client1_private.key | wg pubkey > /etc/wireguard/client1_public.key
chmod 600 /etc/wireguard/client1_private.key
echo "Client private: $(cat /etc/wireguard/client1_private.key)"
echo "Client public:  $(cat /etc/wireguard/client1_public.key)"
Watch out: Never share or expose your server or client private keys. If a private key is compromised, generate a new keypair and replace it immediately — WireGuard has no key revocation mechanism, so the only fix is rotation.

Writing the Server Configuration

The server config lives at /etc/wireguard/wg0.conf. I use the wg0 interface name by convention, which also becomes the name of the systemd service. Replace SERVER_PRIVATE_KEY with the actual key you generated above, and replace eth0 with your VPS's actual network interface name (check with ip link show — on DigitalOcean it's often eth0, but on some providers it may be ens3 or enp1s0).

cat > /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY_HERE

# Replace eth0 with your actual public interface
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

# Client 1
[Peer]
PublicKey = CLIENT1_PUBLIC_KEY_HERE
AllowedIPs = 10.8.0.2/32
EOF

chmod 600 /etc/wireguard/wg0.conf

The PostUp and PostDown lines handle NAT masquerading — this is what lets your VPN clients route all their traffic through the server's public IP. Without these iptables rules, peers can reach each other on the tunnel but can't reach the open internet.

Configuring UFW

I always run UFW on every VPS I touch. Before enabling WireGuard, make sure UFW allows traffic on port 51820/UDP and doesn't block your forwarding rules. UFW can silently break iptables-based forwarding if you're not careful.

# Allow SSH so you don't lock yourself out
ufw allow 22/tcp

# Allow the WireGuard port
ufw allow 51820/udp

# UFW blocks forwarded traffic by default — fix that
sed -i 's/^DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufw

# Also add these lines near the top of /etc/ufw/before.rules,
# BEFORE the *filter block:
# *nat
# :POSTROUTING ACCEPT [0:0]
# -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
# COMMIT

# Enable UFW
ufw --force enable
ufw status verbose
Tip: If you're using the iptables PostUp/PostDown approach in wg0.conf (as I do above), you may not need to manually edit UFW's before.rules — but I've seen systems where UFW's FORWARD policy overrides everything. Always test with a ping to 8.8.8.8 from a connected client before declaring victory.

Starting WireGuard and Enabling at Boot

WireGuard integrates neatly with systemd via the wg-quick wrapper. One command brings the interface up, applies your PostUp rules, and adds the routes.

# Start WireGuard
systemctl start wg-quick@wg0

# Enable it to start on boot
systemctl enable wg-quick@wg0

# Check the status
systemctl status wg-quick@wg0

# See connected peers and handshake times
wg show

You should see the interface come up with the address 10.8.0.1 and the listen port 51820. The wg show output will be sparse until a client connects — once a peer completes a handshake, you'll see its public key, its allowed IPs, and the last handshake timestamp. A handshake older than 3 minutes usually means something is broken on the network path.

Creating the Client Configuration

The client config is just as simple as the server side. You can use this on any WireGuard client app — the official apps for iOS, Android, Windows, macOS, and Linux all accept this format. Replace the placeholder keys with your actual generated keys from earlier.

# Generate this file on your local machine or copy it off the server securely
cat > client1.conf << 'EOF'
[Interface]
PrivateKey = CLIENT1_PRIVATE_KEY_HERE
Address = 10.8.0.2/24
DNS = 1.1.1.1

[Peer]
PublicKey = SERVER_PUBLIC_KEY_HERE
Endpoint = YOUR_VPS_PUBLIC_IP:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
EOF

The AllowedIPs = 0.0.0.0/0 line tells the client to route all traffic through the tunnel — this is the "full tunnel" mode. If you only want to reach resources on the 10.8.0.0/24 network (a "split tunnel"), change that to 10.8.0.0/24 instead. I use full tunnel on my personal devices and split tunnel for work servers where I only need access to internal services.

The PersistentKeepalive = 25 value sends a keepalive packet every 25 seconds. This matters if you're behind NAT — without it, the connection may drop after the NAT table entry expires.

Adding More Peers

Adding a new device is painless. Generate a new keypair, add a [Peer] block to wg0.conf, then reload — no restart required, which means zero downtime for existing connected clients.

# Generate keys for client 2
wg genkey | tee /etc/wireguard/client2_private.key | wg pubkey > /etc/wireguard/client2_public.key
chmod 600 /etc/wireguard/client2_private.key

# Add the new peer to the running interface without restarting
wg set wg0 peer CLIENT2_PUBLIC_KEY_HERE allowed-ips 10.8.0.3/32

# Make the change permanent in wg0.conf
# (add a new [Peer] block with PublicKey and AllowedIPs = 10.8.0.3/32)

# Save the current running state back to the config file
wg-quick save wg0

Verifying the Connection

Once a client connects, run wg show on the server. You'll see the peer's last handshake time and the bytes sent/received. From the client, check your public IP with curl ifconfig.me — it should return your VPS's IP, not your home IP. If it still shows your home IP, the routing rules or DNS configuration isn't working correctly.

I also recommend running ping 10.8.0.1 from the client to confirm the tunnel itself is alive before troubleshooting routing. If the ping works but internet doesn't, the problem is almost always the iptables masquerade rules or IP forwarding not being enabled.

Wrapping Up

WireGuard on a budget VPS is genuinely one of the most satisfying self-hosting projects — it's fast to set up, rock-solid in production, and costs less than a coffee a month to run. My next recommended step is to pair this with Authelia or restrict your WireGuard subnet with firewall rules so that only VPN-connected clients can reach your other self-hosted services. You can also look into wg-easy, a Docker-based WireGuard UI that simplifies peer management if you're adding lots of devices regularly. Start spending more time on your projects and less time managing your infrastructure — create your DigitalOcean account today and have this whole setup running in under half an hour.

DigitalOcean

Discussion

```