Commercial VPN services ask you to trust a third party with all your traffic, and corporate VPN clients are often bloated and slow. WireGuard flips that equati…
Commercial VPN services ask you to trust a third party with all your traffic, and corporate VPN clients are often bloated and slow. WireGuard flips that equation: it's a lean, modern VPN protocol built into the Linux kernel, and running your own server gives you a private, fast, fully-controlled tunnel back into your home network or a secure exit point for your traffic — for the cost of a $5/month VPS. This guide covers a complete server and client setup from scratch.
Why WireGuard Over OpenVPN or IPsec
WireGuard's entire codebase is a fraction of the size of older VPN protocols — around 4,000 lines of code versus OpenVPN's 100,000+. That matters for security: less code means a smaller attack surface and an easier codebase to audit. It also uses modern cryptography by default (no config-time cipher negotiation to get wrong), connects faster, and handles switching networks — like a laptop moving from WiFi to mobile data — far more gracefully than legacy protocols.
Prerequisites / What You'll Need
- A VPS or home server running Ubuntu 22.04/24.04 (a Raspberry Pi works too)
- Root or
sudo access on that machine
- A client device (laptop, phone) to connect from
- Basic familiarity with the terminal
- 20–30 minutes
Step 1: Install WireGuard
On the server:
sudo apt update
sudo apt install -y wireguard
Confirm it installed correctly:
wg --version
Step 2: Generate Server Keys
WireGuard uses public-key cryptography instead of certificates or shared passwords — much simpler to manage:
cd /etc/wireguard
umask 077
wg genkey | tee server_private.key | wg pubkey > server_public.key
The umask 077 ensures the generated key files aren't readable by other users on the system — treat the private key like any other secret.
Step 3: Configure the Server Interface
Create /etc/wireguard/wg0.conf:
[Interface]
PrivateKey = <contents of server_private.key>
Address = 10.8.0.1/24
ListenPort = 51820
SaveConfig = true
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
A few notes:
Address defines the VPN's private subnet — 10.8.0.1 is the server's own address inside the tunnel
PostUp/PostDown enable NAT so traffic from VPN clients can reach the internet through the server — adjust eth0 to match your server's actual public interface (check with ip a)
- Replace
eth0 if your provider uses a different interface name (common alternatives: ens3, enp0s3)
Step 4: Enable IP Forwarding
The server needs to forward packets between the VPN tunnel and the internet:
echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
Without this, clients can connect to the VPN but won't be able to reach anything beyond the server itself.
Step 5: Open the Firewall Port
sudo ufw allow 51820/udp
sudo ufw allow OpenSSH
sudo ufw enable
WireGuard runs over UDP, not TCP — a common mistake is opening the wrong protocol and then wondering why connections silently time out.
Step 6: Start the WireGuard Interface
sudo systemctl enable --now wg-quick@wg0
Verify it's running:
sudo wg show
You should see your interface, public key, and listening port listed.
Step 7: Generate a Client Configuration
On the server, generate a keypair for your first client:
wg genkey | tee client1_private.key | wg pubkey > client1_public.key
Add the client as a peer to the server config:
sudo wg set wg0 peer <client1_public_key> allowed-ips 10.8.0.2/32
Then create the client's own config file, client1.conf:
[Interface]
PrivateKey = <contents of client1_private.key>
Address = 10.8.0.2/32
DNS = 1.1.1.1
[Peer]
PublicKey = <contents of server_public.key>
Endpoint = your-server-ip:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
A couple of settings worth understanding:
AllowedIPs = 0.0.0.0/0 routes all client traffic through the VPN (full tunnel). If you only want access to your home network rather than routing all internet traffic, narrow this to your LAN subnet, e.g. 192.168.1.0/24.
PersistentKeepalive = 25 sends a keepalive packet every 25 seconds — important for clients behind NAT (like most home routers or mobile carriers), which otherwise silently drop the connection after a period of inactivity.
Step 8: Connect from Your Client
Install the WireGuard app for your platform (available for Windows, macOS, iOS, Android, and Linux), then either import client1.conf directly or generate a QR code for mobile devices:
sudo apt install qrencode
qrencode -t ansiutf8 < client1.conf
Scan it with the WireGuard mobile app, or import the file manually on desktop. Activate the tunnel and test connectivity:
ping 10.8.0.1
If that responds, your tunnel is live.
Common Pitfalls / Troubleshooting
- Wrong interface name in NAT rules. If internet access doesn't work through the tunnel but the tunnel itself connects, double check that
eth0 in your PostUp/PostDown rules matches your server's real network interface (ip a will show it).
- Forgetting the firewall is UDP, not TCP. WireGuard exclusively uses UDP — opening
51820/tcp instead of 51820/udp is a common and completely silent mistake.
- Client can't reach the internet, only the server. This almost always traces back to a missing or incorrect
ip_forward setting, or a missing NAT (MASQUERADE) rule.
- Connection drops after a period of idle time. Add
PersistentKeepalive = 25 to the client config — this is especially necessary for mobile clients or anything behind carrier-grade NAT.
- Losing track of assigned client IPs. With multiple clients, keep a simple text file mapping client names to their
10.8.0.x addresses — it's easy to accidentally reuse one and cause a silent conflict.
- DNS leaks. If you want all DNS queries to go through the tunnel too (not just general traffic), make sure the
DNS line in the client config is set, and confirm with a DNS leak test site once connected.
Wrapping Up
You now have a self-hosted VPN that's faster, leaner, and more auditable than most commercial alternatives — and one where you're the only party with access to your traffic. Adding more clients is just a repeat of Steps 7–8 with a new keypair and IP. From here, reasonable next steps include setting up wg-easy or a similar web UI if you're managing many clients, restricting AllowedIPs on individual peers so client devices can't see each other's traffic, and combining this with the Cloudflare Tunnel or server-hardening approaches for a fully locked-down home lab setup.
Further Reading