Running your own WireGuard VPN server means you control what gets logged — nothing, unless you configure it otherwise. The protocol is ~4,000 lines (vs OpenVPN's 70,000+), has been in the Linux kernel since 5.6, and on Ubuntu 22.04 you just install it. This guide covers the full setup: key generation, server config, IP forwarding, NAT, firewall rules, and multi-device client provisioning.
What you need to know before starting:
- Self-hosting eliminates the vendor trust problem — your server logs nothing by default, and no company can be sold to a private equity firm that quietly changes the logging policy.
- What you need: a VPS running Ubuntu 22.04+, UDP port 51820 open inbound, and a non-root sudo user. A $5/month instance handles typical home use. DigitalOcean is the easiest to get started with — clean UI, solid documentation, and one-click Ubuntu droplets. Vultr has more datacenter locations if exit geography matters to you. Hetzner is the cheapest of the three for equivalent specs and runs out of Germany, which puts it under GDPR jurisdiction.
- What you'll build: a full-tunnel VPN that routes all device traffic through your server, with support for multiple clients and mobile QR code provisioning.
- Important caveat: WireGuard traffic is identifiable via deep packet inspection — it doesn't obfuscate itself. For environments where VPN usage itself must be hidden, you need Shadowsocks or obfsproxy instead.
- Harden your VPS first. Running WireGuard on a root-password-only server negates the security benefit. Do the first-24-hours hardening before this guide.
I've run this setup on Ubuntu 22.04 VPS instances across multiple providers. The commands and config patterns below are from actual deployments.
Before You Start: Is Self-Hosting Actually Worth It?
Honest answer: it depends on why you want a VPN.
If your goal is accessing geo-restricted content from multiple countries, a commercial VPN with hundreds of servers is going to serve you better. If your goal is hiding your traffic from your ISP or using public Wi-Fi safely, commercial VPNs are a reasonable option — with some caveats around who you're actually trusting. I wrote about the state of the VPN industry in 2026 and the short version is: the consolidation in that market is real, and knowing who owns your VPN provider is harder than it used to be.
Self-hosting solves the trust problem. Your server doesn't keep logs because you don't configure it to keep logs. Your exit node is wherever your VPS is — pick a provider and a datacenter location that suits you. No vendor can be acquired by a private equity firm and silently change their logging policy.
The tradeoff is that your traffic is still exiting from one static IP. If someone is watching at the network level, they know traffic from your VPS is yours. VPNs — self-hosted or commercial — aren't anonymity tools. They're trust-shifting tools. If you're not clear on that distinction, this piece on DNS and network privacy covers the broader picture.
For users who want a solid commercial option while setting this up, NordVPN and ProtonVPN are the two I'd actually recommend. ProtonVPN has the cleaner privacy track record. NordVPN has the larger server network. But if you're reading a WireGuard setup guide, you probably want to run it yourself — so let's do that.
Installation
WireGuard is in Ubuntu's default repositories. No PPA, no manual build.
sudo apt update && sudo apt install wireguard -y
That's it. The wg and wg-quick commands are now available.
Generating Keys
WireGuard uses Curve25519 keypairs. Each device — server and every client — gets its own key pair. The public key is what you share; the private key never leaves the machine it was generated on.
On the server:
wg genkey | sudo tee /etc/wireguard/privatekey | wg pubkey | sudo tee /etc/wireguard/publickey
sudo chmod 600 /etc/wireguard/privatekey
The private key lands in /etc/wireguard/privatekey, the public key in /etc/wireguard/publickey. Restrict the private key's permissions — that chmod 600 isn't optional.
Check what you have:
sudo cat /etc/wireguard/privatekey
sudo cat /etc/wireguard/publickey
Keep both values handy. You'll need them in the config file.
Server Configuration
Create the config file at /etc/wireguard/wg0.conf. Replace <SERVER_PRIVATE_KEY> with your actual private key:
sudo nano /etc/wireguard/wg0.conf
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
A few things to check before saving:
Address: 10.0.0.1/24 creates a private subnet for the VPN. The server takes .1, your first client will get .2, and so on.
The eth0 in PostUp/PostDown: this is your server's public network interface. It's often eth0, but some VPS providers name it ens3, ens4, or something else. Verify yours:
ip -o -4 route show to default | awk '{print $5}'
Whatever that outputs — put it in place of eth0.
PostUp / PostDown explained: When wg-quick up wg0 runs, PostUp fires the iptables rules that allow forwarding and set up NAT masquerading. This is what lets client traffic actually reach the internet through the VPN. PostDown removes those rules when the interface goes down. The %i variable expands to the interface name (wg0) automatically.
Set correct permissions on the config:
sudo chmod 600 /etc/wireguard/wg0.conf
Enable IP Forwarding
By default, Linux drops packets that aren't destined for the local machine. For a VPN to work — forwarding client traffic to the internet — you need to change that.
Edit /etc/sysctl.conf and uncomment (or add) this line:
net.ipv4.ip_forward=1
Apply it without rebooting:
sudo sysctl -p
If you want IPv6 forwarding too: add net.ipv6.conf.all.forwarding=1 in the same file.
Start WireGuard
sudo wg-quick up wg0
Enable it on boot:
sudo systemctl enable wg-quick@wg0
Check that it's running:
sudo wg show
You should see the interface, public key, listening port, and (once a client connects) peer information. An empty peer list is fine for now.
Add a Client
Generate a key pair on your client machine (laptop, phone, whatever):
wg genkey | tee privatekey | wg pubkey > publickey
On a phone you can skip this — the WireGuard app generates keys for you. But for a desktop client, run the above.
Now add a [Peer] block to the server config. Take your client's public key and add:
sudo nano /etc/wireguard/wg0.conf
[Peer]
PublicKey = <CLIENT_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32
AllowedIPs = 10.0.0.2/32 tells the server to route traffic to/from that single VPN IP to this client. Each client gets a unique IP (.2, .3, .4, etc.).
Reload WireGuard to pick up the new peer:
sudo wg syncconf wg0 <(wg-quick strip wg0)
This applies the change without dropping existing connections.
Client Configuration File
The client needs its own .conf file. Create this on the client machine:
[Interface]
Address = 10.0.0.2/24
PrivateKey = <CLIENT_PRIVATE_KEY>
DNS = 1.1.1.1
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = <YOUR_VPS_IP>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
AllowedIPs = 0.0.0.0/0 routes all traffic through the VPN (full tunnel). If you only want VPN traffic for specific subnets — split tunneling — replace it with those CIDRs.
PersistentKeepalive = 25 keeps the tunnel alive through NAT. Most consumer connections are behind NAT; this prevents the session from timing out after 25 seconds of silence.
For mobile, the WireGuard app can import .conf files directly or scan a QR code. To generate a QR code on the server:
sudo apt install qrencode
qrencode -t ansiutf8 < /etc/wireguard/client1.conf
Verify It's Working
Connect from the client, then check the server:
sudo wg show
You should see the peer with a "latest handshake" timestamp and bytes transferred. No handshake means the client isn't reaching the server — check that UDP 51820 is open in your VPS firewall.
Then run a DNS leak test. dnsleaktest.com is straightforward. Your public IP should show the VPS IP, and your DNS servers should reflect what you set in the client's DNS = line. If you're leaking your ISP's DNS servers, the tunnel isn't carrying DNS — worth understanding why.
Adding More Clients
Same process each time: generate a key pair, add a [Peer] block to the server config with a new AllowedIPs address (.3, .4, etc.), run wg syncconf. Each client gets its own .conf file pointing at the server.
There's no theoretical limit on clients. Practically, your VPS bandwidth is the bottleneck. A $5/month VPS with 1TB of transfer handles most home use cases comfortably.
One More Thing
WireGuard doesn't hide the fact that you're using WireGuard. Deep packet inspection can identify WireGuard traffic. For most use cases — privacy from your ISP, secure access to your home network, safe coffee shop browsing — that's fine. If you're in an environment where VPN usage itself needs to be hidden, that's a different problem requiring different tools (obfsproxy, Shadowsocks, etc.).
For typical self-hosting scenarios, this setup is solid. You own the server, you control the keys, and you know exactly what's being logged — nothing, unless you set it up otherwise.