How to Set Up WireGuard VPN Server on a VPS: Full Configuration Guide
WireGuard is the modern VPN protocol that has replaced OpenVPN for most self-hosted use cases — it’s faster, simpler, uses less battery on mobile devices, and has a codebase small enough to be audited by a single engineer. The first guide in this series covered the basics. This guide goes deeper: multi-client configuration, split tunneling, kill switch, DNS leak prevention, road warrior setup, and site-to-site networking.
Why WireGuard Over OpenVPN in 2025?
| Feature | WireGuard | OpenVPN |
|---|---|---|
| Codebase size | ~4,000 lines | ~100,000+ lines |
| Throughput | Up to 10 Gbps | ~1 Gbps max |
| Handshake time | <100ms | 1–2 seconds |
| Battery (mobile) | Minimal drain | Higher drain |
| Kernel integration | Built into Linux 5.6+ | Userspace |
| Roaming (IP change) | Seamless | Reconnects required |
Part 1: Full Server Setup
Step 1: Install WireGuard
sudo apt update && sudo apt install wireguard wireguard-tools -y
Step 2: Generate Server Keys
cd /etc/wireguard
umask 077
# Generate server private and public keys
wg genkey | tee server_private.key | wg pubkey > server_public.key
# Generate a preshared key (extra layer of security)
wg genpsk > preshared.key
echo "Server private key: $(cat server_private.key)"
echo "Server public key: $(cat server_public.key)"
Step 3: Find Your Network Interface
# Find the interface with the default route
ip route | grep default | awk '{print $5}'
# Usually: eth0, ens3, enp0s3
Step 4: Create Server Configuration
sudo nano /etc/wireguard/wg0.conf
[Interface]
# Server IP in the VPN subnet
Address = 10.10.0.1/24
# Listen on this port
ListenPort = 51820
# Server private key
PrivateKey = SERVER_PRIVATE_KEY_HERE
# DNS server for VPN clients (use your preferred resolver)
DNS = 1.1.1.1, 8.8.8.8
# Enable NAT — replace eth0 with your actual interface
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -A FORWARD -o wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -o wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
Step 5: Enable IP Forwarding
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
echo "net.ipv6.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
Step 6: Start WireGuard
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo wg show
Part 2: Multi-Client Setup
Generate keys for each client
# Generate keys for client 1 (laptop)
wg genkey | tee client1_private.key | wg pubkey > client1_public.key
wg genkey | tee client2_private.key | wg pubkey > client2_public.key
wg genkey | tee client3_private.key | wg pubkey > client3_public.key
Add peers to server config
sudo nano /etc/wireguard/wg0.conf
Append a [Peer] section for each client:
# Client 1 — Laptop
[Peer]
PublicKey = CLIENT1_PUBLIC_KEY
PresharedKey = PRESHARED_KEY
AllowedIPs = 10.10.0.2/32
# Optional: give client a fixed DNS name in comments
# Description: Alice's MacBook
# Client 2 — Phone
[Peer]
PublicKey = CLIENT2_PUBLIC_KEY
PresharedKey = PRESHARED_KEY
AllowedIPs = 10.10.0.3/32
# Client 3 — Home Server
[Peer]
PublicKey = CLIENT3_PUBLIC_KEY
PresharedKey = PRESHARED_KEY
AllowedIPs = 10.10.0.4/32
sudo systemctl reload wg-quick@wg0
sudo wg show # Verify peers are listed
Part 3: Client Configurations
Full-tunnel client (all traffic through VPN)
# client1.conf — send ALL traffic through the VPN
[Interface]
PrivateKey = CLIENT1_PRIVATE_KEY
Address = 10.10.0.2/24
DNS = 1.1.1.1
[Peer]
PublicKey = SERVER_PUBLIC_KEY
PresharedKey = PRESHARED_KEY
Endpoint = YOUR_VPS_IP:51820
AllowedIPs = 0.0.0.0/0, ::/0 # Route ALL traffic through VPN
PersistentKeepalive = 25
Split-tunnel client (VPN traffic only)
# client1-split.conf — only VPN subnet traffic goes through VPN
[Interface]
PrivateKey = CLIENT1_PRIVATE_KEY
Address = 10.10.0.2/24
[Peer]
PublicKey = SERVER_PUBLIC_KEY
PresharedKey = PRESHARED_KEY
Endpoint = YOUR_VPS_IP:51820
AllowedIPs = 10.10.0.0/24 # Only route VPN subnet traffic
PersistentKeepalive = 25
Split tunneling use case: Access resources on your home network or VPS network via VPN, while normal internet traffic goes directly. Lower latency for most browsing, VPN only for specific internal resources.
Part 4: Kill Switch
A kill switch blocks all internet traffic if the VPN connection drops — preventing accidental data leakage on an unencrypted connection:
# Kill switch client config (Linux)
[Interface]
PrivateKey = CLIENT1_PRIVATE_KEY
Address = 10.10.0.2/24
DNS = 1.1.1.1
# Kill switch: block all non-VPN traffic
PostUp = iptables -I OUTPUT ! -o wg0 -m mark ! --mark $(wg show wg0 fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -I OUTPUT ! -o wg0 -m mark ! --mark $(wg show wg0 fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PreDown = iptables -D OUTPUT ! -o wg0 -m mark ! --mark $(wg show wg0 fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -D OUTPUT ! -o wg0 -m mark ! --mark $(wg show wg0 fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
[Peer]
PublicKey = SERVER_PUBLIC_KEY
PresharedKey = PRESHARED_KEY
Endpoint = YOUR_VPS_IP:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
Part 5: DNS Leak Prevention
# Test for DNS leaks on client
# 1. Connect to WireGuard
# 2. Visit https://dnsleaktest.com
# 3. Run extended test — should only show VPS IP and your chosen DNS resolver
# If you see your ISP's DNS servers, fix with:
# In client config, set:
DNS = 1.1.1.1, 8.8.8.8
# For maximum privacy, run your own DNS resolver on the VPS:
sudo apt install unbound -y
Set up Unbound as a local DNS resolver on VPS
sudo nano /etc/unbound/unbound.conf.d/wireguard.conf
server:
interface: 10.10.0.1
access-control: 10.10.0.0/24 allow
do-ip4: yes
do-ip6: yes
do-udp: yes
do-tcp: yes
hide-identity: yes
hide-version: yes
use-syslog: yes
sudo systemctl enable unbound
sudo systemctl restart unbound
Update WireGuard server config to use Unbound:
# In [Interface] section of wg0.conf:
DNS = 10.10.0.1 # Point to Unbound running on the VPS
Part 6: Site-to-Site VPN (Connect Two VPS Instances)
Connect two VPS instances (e.g., USA VPS and Hong Kong VPS) so services on each can reach the other privately:
# On USA VPS (server A) — add HK VPS as a peer
[Peer]
# Hong Kong VPS
PublicKey = HK_VPS_PUBLIC_KEY
AllowedIPs = 10.10.0.10/32, 192.168.2.0/24 # HK VPS IP + its LAN subnet
Endpoint = HK_VPS_IP:51820
# On HK VPS (server B) — add USA VPS as a peer
[Peer]
# USA VPS
PublicKey = USA_VPS_PUBLIC_KEY
AllowedIPs = 10.10.0.1/32, 192.168.1.0/24
Endpoint = USA_VPS_IP:51820
Both servers can now reach each other’s private networks through the encrypted WireGuard tunnel.
Part 7: QR Code for Mobile Clients
sudo apt install qrencode -y
# Generate QR code from client config file
qrencode -t ansiutf8 < client1.conf
# Or save as PNG
qrencode -t PNG -o client1-qr.png < client1.conf
Scan the QR code with the WireGuard iOS or Android app to import the configuration instantly — no manual typing required.
Part 8: WireGuard Management Script
nano ~/wg-manage.sh
#!/bin/bash
# Simple WireGuard client management
WG_DIR="/etc/wireguard"
VPN_SUBNET="10.10.0"
SERVER_PUBKEY=$(cat $WG_DIR/server_public.key)
SERVER_IP="YOUR_VPS_IP"
SERVER_PORT="51820"
add_client() {
NAME=$1
# Find next available IP
LAST_IP=$(grep -oP '10\.10\.0\.\K\d+' $WG_DIR/wg0.conf | sort -n | tail -1)
NEXT_IP=$((LAST_IP + 1))
# Generate keys
PRIV=$(wg genkey)
PUB=$(echo $PRIV | wg pubkey)
PSK=$(wg genpsk)
# Add to server config
cat >> $WG_DIR/wg0.conf << EOF # $NAME [Peer] PublicKey = $PUB PresharedKey = $PSK AllowedIPs = $VPN_SUBNET.$NEXT_IP/32 EOF # Create client config cat > $WG_DIR/clients/$NAME.conf << EOF
[Interface]
PrivateKey = $PRIV
Address = $VPN_SUBNET.$NEXT_IP/24
DNS = 1.1.1.1
[Peer]
PublicKey = $SERVER_PUBKEY
PresharedKey = $PSK
Endpoint = $SERVER_IP:$SERVER_PORT
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
EOF
wg syncconf wg0 <(wg-quick strip wg0)
echo "✅ Client '$NAME' created with IP $VPN_SUBNET.$NEXT_IP"
qrencode -t ansiutf8 < $WG_DIR/clients/$NAME.conf
}
mkdir -p $WG_DIR/clients
add_client "$1"
chmod +x ~/wg-manage.sh
sudo bash ~/wg-manage.sh "alice-phone"
Monitoring and Troubleshooting
# Show connected peers and traffic stats
sudo wg show
# Show with handshake times
sudo wg show wg0
# Monitor live connections
watch -n 2 "sudo wg show"
# Check if VPN is routing correctly on client
curl --interface wg0 https://ipinfo.io/ip
# Should show your VPS IP, not your local IP
# Check firewall rules
sudo iptables -L -n -v | grep -i wg
Final Thoughts
WireGuard’s simplicity doesn’t mean it lacks capability. Multi-client configurations, split tunneling, kill switches, DNS leak prevention, and site-to-site networking are all achievable with straightforward configuration files. For a self-hosted VPN, WireGuard on a VPS.DO KVM VPS is the optimal combination: the kernel-level integration requires true virtualization (available on all KVM VPS), and the 1 Gbps port delivers throughput that exceeds any commercial VPN service.