How to Set Up WireGuard VPN Server on a VPS: Full Configuration Guide

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.

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!