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:
- Privacy — Encrypt all traffic between your devices and the VPN server, preventing ISP snooping and protecting yourself on public WiFi
- Secure remote access — Connect to internal services (databases, admin panels, monitoring dashboards) without exposing them to the internet
- Geo-unblocking — Access region-restricted content by routing traffic through a server in the target country
- Team access — Provide secure connectivity for a small team without paying per-user SaaS VPN fees
- Development and testing — Test geolocation-dependent features by routing traffic through different regions
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:
| Location | Best For | Typical Latency |
|---|---|---|
| New York (NYC) | US-based services, North American users | < 20 ms from US East Coast |
| London | UK/Europe services, GDPR-compliant traffic | < 15 ms from Western Europe |
| Frankfurt | Central Europe, EU data residency | < 10 ms from DACH region |
| Singapore | Asia-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:
| Metric | Without VPN | With WireGuard |
|---|---|---|
| Download speed | 800 Mbps | 650–750 Mbps |
| Upload speed | 400 Mbps | 350–400 Mbps |
| Latency overhead | — | 1–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:
- Database replication — Securely replicate MySQL or PostgreSQL between a primary in Frankfurt and a replica in New York, all traffic encrypted over the WireGuard tunnel
- Application clusters — Connect backend servers across multiple MassiveGRID locations into a private mesh network
- Backup transport — Push encrypted backups from your production server to a backup server in a different datacenter
- Monitoring — Allow your monitoring server to scrape metrics from all your servers over encrypted connections, without exposing metrics endpoints to the public internet
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:
- Change the default port — While not security through obscurity, using a non-standard port (e.g., 41194) reduces automated scanning noise in your logs
- Lock down file permissions — All key files and configuration should be readable only by root:
sudo chmod 600 /etc/wireguard/wg0.conf
sudo chmod 600 /etc/wireguard/*.key
sudo chmod 700 /etc/wireguard/clients/
- Monitor connections — Set up a simple script that alerts you when an unexpected peer connects:
#!/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
- Rotate keys periodically — While WireGuard handles key rotation for session keys automatically, rotating peer keys every 6–12 months is good practice. Generate new keys, update both the server and client configurations, and delete the old keys.
- Fail2ban for WireGuard — WireGuard itself does not produce auth failure logs (by design — it is silent to unauthorized packets). However, you should still run Fail2ban on SSH and any other exposed services on the VPS.
WireGuard vs OpenVPN: Why WireGuard Wins
If you are considering between WireGuard and OpenVPN for your VPS, here is a direct comparison:
| Feature | WireGuard | OpenVPN |
|---|---|---|
| Codebase size | ~4,000 lines | ~100,000+ lines |
| Protocol | UDP only | UDP or TCP |
| Encryption | ChaCha20-Poly1305 | Configurable (many options) |
| Connection speed | Near-instant | 5–15 seconds |
| Throughput overhead | 5–15% | 20–30% |
| Latency overhead | 1–3 ms | 5–15 ms |
| Configuration | Simple (one config file) | Complex (certificates, multiple files) |
| Roaming support | Seamless | Reconnects required |
| Battery impact (mobile) | Minimal | Significant |
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.