Why Self-Host a VPN?

Commercial VPN services are everywhere, but they come with a fundamental trust problem: you are routing all your internet traffic through someone else’s servers. Their privacy policies are only as good as their word. Their infrastructure is shared with thousands of other users. And when a popular VPN provider gets breached or compelled to hand over logs, every user is affected.

Running your own WireGuard VPN server on a VPS gives you complete control. You own the server, you control the logs (or lack thereof), and you know exactly who has access. Practical use cases for a self-hosted VPN include:

Choosing a Server Location

Your VPN server’s location determines both latency and the IP address seen by the websites you visit. MassiveGRID offers VPS in four strategic locations:

LocationBest ForTypical Latency
New York (NYC)US-based services, North American users< 20 ms from US East Coast
LondonUK/Europe services, GDPR-compliant traffic< 15 ms from Western Europe
FrankfurtCentral Europe, EU data residency< 10 ms from DACH region
SingaporeAsia-Pacific services, APAC users< 30 ms from Southeast Asia

For personal VPN use, choose the location closest to you for the best speeds. For geo-unblocking, choose the location of the content you want to access. For secure remote access to your other MassiveGRID servers, put the VPN in the same datacenter for single-digit millisecond latency between your VPN and your services.

A basic WireGuard VPN server needs minimal resources — 1 vCPU and 1 GB RAM is more than enough for personal use or a small team. WireGuard is extremely lightweight compared to OpenVPN.

Installing WireGuard on Ubuntu 24.04

WireGuard is included in the Ubuntu 24.04 kernel, so installation is straightforward:

sudo apt update
sudo apt install wireguard wireguard-tools -y

Verify the WireGuard kernel module is loaded:

sudo modprobe wireguard
lsmod | grep wireguard

You should see wireguard in the output. On MassiveGRID VPS instances running Ubuntu 24.04, the kernel module is available out of the box.

Key Generation

WireGuard uses Curve25519 key pairs for encryption. Generate the server keys first:

# Generate server private key
wg genkey | sudo tee /etc/wireguard/server_private.key
sudo chmod 600 /etc/wireguard/server_private.key

# Derive the server public key
sudo cat /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key

Generate keys for your first client:

# Generate client private key
wg genkey | tee /etc/wireguard/client1_private.key
cat /etc/wireguard/client1_private.key | wg pubkey | tee /etc/wireguard/client1_public.key

Also generate a preshared key for additional security (quantum-resistant key exchange):

wg genpsk | sudo tee /etc/wireguard/client1_preshared.key

Keep private keys secret. Never share them or transmit them over unencrypted channels. The preshared key adds a layer of symmetric cryptography on top of Curve25519, providing post-quantum security.

Server Configuration

Create the WireGuard interface configuration:

sudo nano /etc/wireguard/wg0.conf
[Interface]
# Server private key
PrivateKey = SERVER_PRIVATE_KEY_HERE

# VPN subnet — use a private range that does not conflict with your LAN
Address = 10.0.0.1/24

# WireGuard listen port
ListenPort = 51820

# Save configuration changes on shutdown
SaveConfig = false

# NAT rules — applied when the interface comes up/down
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -A FORWARD -o wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -o wg0 -j ACCEPT

[Peer]
# Client 1
PublicKey = CLIENT1_PUBLIC_KEY_HERE
PresharedKey = CLIENT1_PRESHARED_KEY_HERE
AllowedIPs = 10.0.0.2/32

Replace the placeholder keys with the actual keys you generated. Replace eth0 with your server’s main network interface — check it with ip route show default.

The AllowedIPs = 10.0.0.2/32 in the peer section tells the server that this client owns the IP address 10.0.0.2 within the VPN subnet.

IP Forwarding and NAT

For the VPN to work as a gateway (routing client traffic to the internet), you need to enable IP forwarding:

# Enable immediately
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv6.conf.all.forwarding=1

# Make persistent across reboots
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
echo "net.ipv6.conf.all.forwarding = 1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system

Open the WireGuard port in the firewall:

sudo ufw allow 51820/udp
sudo ufw allow OpenSSH
sudo ufw enable

Now start the WireGuard interface:

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0

# Verify it is running
sudo wg show

You should see the interface listed with its public key, listening port, and any connected peers.

Client Configuration

WireGuard clients are available on every major platform. The configuration file format is the same everywhere.

Linux Client

sudo apt install wireguard -y
sudo nano /etc/wireguard/wg0.conf
[Interface]
PrivateKey = CLIENT1_PRIVATE_KEY_HERE
Address = 10.0.0.2/24
DNS = 10.0.0.1

[Peer]
PublicKey = SERVER_PUBLIC_KEY_HERE
PresharedKey = CLIENT1_PRESHARED_KEY_HERE
Endpoint = YOUR_SERVER_IP:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
sudo wg-quick up wg0

# Verify connection
curl ifconfig.me  # Should show your VPN server's IP

macOS and Windows

Download the official WireGuard app from wireguard.com. Create a new tunnel and paste the same configuration content as the Linux client above. On macOS, you can also use the App Store version. On Windows, the application adds a system tray icon for quick connect/disconnect.

iOS and Android

Install the WireGuard app from the App Store or Google Play. You can either type in the configuration manually or generate a QR code on the server for easy import:

sudo apt install qrencode -y

# Generate QR code from the client config
qrencode -t ansiutf8 << EOF
[Interface]
PrivateKey = CLIENT1_PRIVATE_KEY_HERE
Address = 10.0.0.2/24
DNS = 10.0.0.1

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

Open the WireGuard app on your phone, tap the + button, select “Create from QR code,” and scan the terminal output.

The AllowedIPs = 0.0.0.0/0, ::/0 setting routes ALL traffic through the VPN (full tunnel). If you only want to access the VPN server and its local network, change this to 10.0.0.0/24 (split tunnel).

The PersistentKeepalive = 25 setting sends a keepalive packet every 25 seconds. This is important for clients behind NAT (which is almost every home and mobile connection) to keep the UDP mapping alive.

Private DNS with Unbound

Without configuring DNS on the VPN, your DNS queries still go to your ISP or public DNS resolvers — leaking the domains you visit even though the traffic itself is encrypted. Set up Unbound as a local recursive DNS resolver on the VPN server:

sudo apt install unbound unbound-host -y

Configure Unbound to listen on the WireGuard interface:

sudo nano /etc/unbound/unbound.conf.d/wireguard.conf
server:
    interface: 10.0.0.1
    interface: 127.0.0.1
    access-control: 10.0.0.0/24 allow
    access-control: 127.0.0.0/8 allow

    # Privacy and security
    hide-identity: yes
    hide-version: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    use-caps-for-id: yes
    val-clean-additional: yes

    # Performance
    num-threads: 2
    msg-cache-size: 16m
    rrset-cache-size: 32m
    cache-min-ttl: 300
    cache-max-ttl: 86400
    prefetch: yes

    # Block common ad/tracking domains (optional)
    # include: /etc/unbound/blocklist.conf
sudo systemctl enable unbound
sudo systemctl restart unbound

# Test DNS resolution through Unbound
dig @10.0.0.1 example.com

Now all VPN clients using DNS = 10.0.0.1 in their configuration will resolve DNS through the VPN server’s Unbound instance. No DNS leaks.

Kill Switch Configuration

A kill switch prevents traffic from leaking outside the VPN tunnel if the connection drops. On Linux clients, use firewall rules:

# Save these as a script: /usr/local/bin/vpn-killswitch.sh
#!/bin/bash
VPN_SERVER="YOUR_SERVER_IP"
VPN_PORT="51820"

# Block all traffic except VPN
sudo iptables -F OUTPUT
sudo iptables -A OUTPUT -o lo -j ACCEPT
sudo iptables -A OUTPUT -o wg0 -j ACCEPT
sudo iptables -A OUTPUT -d $VPN_SERVER -p udp --dport $VPN_PORT -j ACCEPT
sudo iptables -A OUTPUT -j DROP

echo "Kill switch enabled. Only VPN traffic allowed."

To disable the kill switch:

sudo iptables -F OUTPUT
sudo iptables -A OUTPUT -j ACCEPT

On macOS and iOS, the WireGuard app has a built-in “On-demand” feature. Enable it to automatically connect to the VPN on any network (or specific networks like public WiFi). On Windows, the WireGuard app’s “Block untunneled traffic” option provides the same kill switch functionality.

Adding Multiple Clients and Key Management

Each device that connects to your VPN needs its own unique key pair and IP address. Here is a streamlined process for adding new clients:

#!/bin/bash
# /usr/local/bin/wg-add-client.sh
# Usage: wg-add-client.sh client_name client_ip

CLIENT_NAME=$1
CLIENT_IP=$2
SERVER_PUBLIC_KEY=$(cat /etc/wireguard/server_public.key)
SERVER_ENDPOINT="YOUR_SERVER_IP:51820"

# Generate client keys
wg genkey | tee /etc/wireguard/clients/${CLIENT_NAME}_private.key
cat /etc/wireguard/clients/${CLIENT_NAME}_private.key | wg pubkey | tee /etc/wireguard/clients/${CLIENT_NAME}_public.key
wg genpsk | tee /etc/wireguard/clients/${CLIENT_NAME}_preshared.key

CLIENT_PRIVATE=$(cat /etc/wireguard/clients/${CLIENT_NAME}_private.key)
CLIENT_PUBLIC=$(cat /etc/wireguard/clients/${CLIENT_NAME}_public.key)
CLIENT_PSK=$(cat /etc/wireguard/clients/${CLIENT_NAME}_preshared.key)

# Add peer to running server (no restart needed)
sudo wg set wg0 peer "$CLIENT_PUBLIC" preshared-key /etc/wireguard/clients/${CLIENT_NAME}_preshared.key allowed-ips "${CLIENT_IP}/32"

# Also add to wg0.conf for persistence across restarts
cat >> /etc/wireguard/wg0.conf << PEER

[Peer]
# ${CLIENT_NAME}
PublicKey = ${CLIENT_PUBLIC}
PresharedKey = ${CLIENT_PSK}
AllowedIPs = ${CLIENT_IP}/32
PEER

# Generate client configuration file
cat > /etc/wireguard/clients/${CLIENT_NAME}.conf << CLIENTCONF
[Interface]
PrivateKey = ${CLIENT_PRIVATE}
Address = ${CLIENT_IP}/24
DNS = 10.0.0.1

[Peer]
PublicKey = ${SERVER_PUBLIC_KEY}
PresharedKey = ${CLIENT_PSK}
Endpoint = ${SERVER_ENDPOINT}
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
CLIENTCONF

# Generate QR code
qrencode -t ansiutf8 < /etc/wireguard/clients/${CLIENT_NAME}.conf

echo "Client '${CLIENT_NAME}' added with IP ${CLIENT_IP}"
echo "Config saved to: /etc/wireguard/clients/${CLIENT_NAME}.conf"

Set up the clients directory first:

sudo mkdir -p /etc/wireguard/clients
sudo chmod 700 /etc/wireguard/clients

Usage example:

sudo bash /usr/local/bin/wg-add-client.sh laptop 10.0.0.3
sudo bash /usr/local/bin/wg-add-client.sh phone 10.0.0.4
sudo bash /usr/local/bin/wg-add-client.sh tablet 10.0.0.5

To revoke a client, remove their peer from the running interface and from the configuration file:

sudo wg set wg0 peer CLIENT_PUBLIC_KEY_HERE remove
# Then edit /etc/wireguard/wg0.conf and remove the [Peer] block

Performance Testing

After setting up the VPN, verify that performance is acceptable. WireGuard is significantly faster than OpenVPN due to its modern cryptographic primitives and kernel-space implementation, but your actual speeds depend on the VPS’s network capacity and the distance between client and server.

Test throughput using iperf3. On the server:

sudo apt install iperf3 -y
iperf3 -s -B 10.0.0.1

On the client:

iperf3 -c 10.0.0.1 -t 30

Typical results on a MassiveGRID VPS with good connectivity:

MetricWithout VPNWith WireGuard
Download speed800 Mbps650–750 Mbps
Upload speed400 Mbps350–400 Mbps
Latency overhead1–3 ms additional

WireGuard typically adds only 1–3 ms of latency and reduces throughput by 5–15%, far less than OpenVPN which commonly adds 15–30% overhead. If you see significantly worse performance, check for CPU bottlenecks on the VPS (WireGuard is CPU-bound during encryption) or packet loss on the network path.

Verify there are no DNS leaks by visiting dnsleaktest.com while connected. You should only see your VPN server’s IP address, not your ISP’s DNS servers.

Summary

A self-hosted WireGuard VPN is one of the most practical things you can run on a VPS. It takes less than 30 minutes to set up, uses almost no resources (a 1 vCPU / 1 GB VPS is plenty), and gives you privacy, security, and remote access without trusting a third party. Deploy a MassiveGRID VPS in the location closest to you, follow this guide, and you will have a production-grade VPN running in under an hour. For teams that need dedicated performance guarantees, a Dedicated VPS ensures consistent throughput even under heavy concurrent use.

MassiveGRID Ubuntu VPS includes: Ubuntu 24.04 LTS pre-installed · Proxmox HA cluster with automatic failover · Ceph 3x replicated NVMe storage · Independent CPU/RAM/storage scaling · 12 Tbps DDoS protection · 4 global datacenter locations · 100% uptime SLA · 24/7 human support rated 9.5/10

Deploy a self-managed VPS — from $1.99/mo
Need dedicated resources? — from $8.30/mo
Want fully managed hosting? — we handle everything

Advanced Configuration: Split Tunneling

Full tunnel mode (routing all traffic through the VPN) is the default configuration we set up above. But sometimes you only want specific traffic to go through the VPN — for example, accessing internal services while keeping general browsing on your local connection for better speed.

To configure split tunneling, change the AllowedIPs on the client side:

[Peer]
PublicKey = SERVER_PUBLIC_KEY_HERE
PresharedKey = CLIENT1_PRESHARED_KEY_HERE
Endpoint = YOUR_SERVER_IP:51820

# Full tunnel — all traffic through VPN:
# AllowedIPs = 0.0.0.0/0, ::/0

# Split tunnel — only VPN subnet and specific ranges:
AllowedIPs = 10.0.0.0/24, 192.168.1.0/24

PersistentKeepalive = 25

With split tunneling, only traffic destined for 10.0.0.0/24 (the VPN subnet) and 192.168.1.0/24 (your server’s private network, for example) routes through the VPN. Everything else goes directly through your local internet connection.

This is ideal for developers who need to access staging servers or databases through the VPN while keeping their general browsing fast and local.

WireGuard for Secure Server-to-Server Communication

WireGuard is not limited to client-to-server VPN setups. It is equally valuable for creating encrypted links between servers. Common use cases:

For server-to-server links, configure both ends as peers and set AllowedIPs to each other’s VPN address (not 0.0.0.0/0). Both sides should have PersistentKeepalive set if either is behind NAT.

Hardening WireGuard

WireGuard is secure by default — it uses state-of-the-art cryptography (Curve25519, ChaCha20-Poly1305, BLAKE2s) and has a tiny attack surface (about 4,000 lines of kernel code). But you can still harden the surrounding environment:

sudo chmod 600 /etc/wireguard/wg0.conf
sudo chmod 600 /etc/wireguard/*.key
sudo chmod 700 /etc/wireguard/clients/
#!/bin/bash
# /usr/local/bin/wg-monitor.sh
KNOWN_PEERS=3  # Expected number of peers

CURRENT=$(sudo wg show wg0 peers | wc -l)
if [ "$CURRENT" -ne "$KNOWN_PEERS" ]; then
    echo "WARNING: Expected $KNOWN_PEERS peers, found $CURRENT" | mail -s "WireGuard Alert" admin@example.com
fi

WireGuard vs OpenVPN: Why WireGuard Wins

If you are considering between WireGuard and OpenVPN for your VPS, here is a direct comparison:

FeatureWireGuardOpenVPN
Codebase size~4,000 lines~100,000+ lines
ProtocolUDP onlyUDP or TCP
EncryptionChaCha20-Poly1305Configurable (many options)
Connection speedNear-instant5–15 seconds
Throughput overhead5–15%20–30%
Latency overhead1–3 ms5–15 ms
ConfigurationSimple (one config file)Complex (certificates, multiple files)
Roaming supportSeamlessReconnects required
Battery impact (mobile)MinimalSignificant

WireGuard’s only limitation is the UDP-only design — it cannot fall back to TCP, which means it can be blocked by restrictive firewalls (some corporate networks and countries). For every other scenario, WireGuard is the clear winner.

Deploy a MassiveGRID VPS and you will have your own private VPN running in under 30 minutes. With datacenters in New York, London, Frankfurt, and Singapore, you can set up VPN endpoints in every major region — or connect them together into a private mesh network spanning all four locations.