All posts How-to

How to set up a WireGuard VPN on a VPS

A copy-pasteable walkthrough for running your own WireGuard server on any Linux VPS. Real commands, real config, and the gotchas nobody mentions in the official docs.

wg0 WIREGUARD

WireGuard is the easiest VPN you'll ever set up - and "easy" here is doing a lot of heavy lifting. The whole codebase is under 4,000 lines, it lives in the Linux kernel, the crypto is opinionated (no knobs to misconfigure), and a working server-and-client setup takes about fifteen minutes if you know what you're doing. This guide is for the case where you don't.

By the end of it you'll have a private WireGuard tunnel running on a Linux VPS, your phone and laptop will connect to it, and you'll understand each piece well enough to debug it when something goes sideways. Every command below is copy-pasteable. The example uses Ubuntu 22.04 or Debian 12, but it works on basically any recent Linux distribution with minimal changes.

What this gets you

A WireGuard tunnel from your devices to a VPS gives you three things that commercial VPN providers charge monthly for:

  • A trusted exit. Coffee-shop Wi-Fi, hotel networks, sketchy cellular - all routed through your own machine instead of whatever the local network happens to be doing.
  • A stable public IP. Useful if you need a known endpoint for self-hosted services, DNS like Pi-hole, port forwarding to a home NAS, or whitelisted access to corporate resources.
  • Real, auditable privacy. No "no-logs policy" you have to take on faith - the no-logs policy is just journalctl, and you decide what it keeps.

Prerequisites

  • A Linux VPS. Pulsar Nano at $3/month is enough for one user with several devices; bandwidth is the only real constraint.
  • Ubuntu 22.04 / 24.04 or Debian 12 (both ship kernels with WireGuard already compiled in).
  • Root or sudo access.
  • A WireGuard client on the device you want to connect: official apps are available for iOS, Android, macOS, Windows, and Linux.

Step 1: Install WireGuard

SSH into your VPS and install the package. The kernel module is already there; we just need the userspace tools.

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

That's it. WireGuard is installed but not configured. Move on.

Step 2: Generate server keys

WireGuard uses public-key crypto - each peer (server or client) gets a private and a public key. Anyone with the public key can talk to that peer; only the private key can decrypt incoming traffic. We'll keep the private key in a file with strict permissions.

cd /etc/wireguard
umask 077
wg genkey | tee server_private.key | wg pubkey > server_public.key

The umask 077 ensures the resulting files are mode 600 (owner read/write only). Run cat server_private.key server_public.key if you want to look at them - they're base64-encoded 32-byte values.

Step 3: Write the server config

Create the file /etc/wireguard/wg0.conf:

sudo nano /etc/wireguard/wg0.conf

Paste in this template (we'll fill in the peer section in Step 5):

[Interface]
# The server's private key (paste from server_private.key)
PrivateKey = REPLACE_WITH_SERVER_PRIVATE_KEY

# Tunnel subnet - this is the private network the VPN uses internally.
# 10.66.0.1/24 is a safe default; change if it conflicts with your LAN.
Address = 10.66.0.1/24

# UDP port WireGuard listens on. 51820 is the default; change for obscurity.
ListenPort = 51820

# Hook scripts: enable NAT when the tunnel comes up, disable on shutdown.
# eth0 is the public interface - check with `ip route show default` if unsure.
PostUp   = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

# --- Client peers will be added below this line in Step 5 ---

A few things to swap before saving:

  • Replace REPLACE_WITH_SERVER_PRIVATE_KEY with the contents of server_private.key (just the base64 string, no quotes).
  • Confirm your public interface is actually eth0 with ip route show default. On many cloud VPS providers it's ens3, enp1s0, or similar. If it isn't eth0, swap it in the PostUp/PostDown lines.

Step 4: Enable IP forwarding

WireGuard relays packets between the tunnel and the internet, which means the kernel needs to forward IPv4 traffic between interfaces. By default, Linux doesn't.

sudo nano /etc/sysctl.conf

Find and uncomment (or add) this line:

net.ipv4.ip_forward=1

Apply it without rebooting:

sudo sysctl -p

If you also want IPv6 forwarding (recommended), uncomment net.ipv6.conf.all.forwarding=1 in the same file.

Step 5: Add your first client (peer)

On the device you want to connect - phone, laptop, whatever - install the WireGuard app and generate a keypair there. Or generate it on the server and transfer the config to the device; both are fine. We'll do it on the server since you're already SSH'd in.

cd /etc/wireguard
wg genkey | tee client1_private.key | wg pubkey > client1_public.key

Now append a [Peer] block to /etc/wireguard/wg0.conf:

[Peer]
# Client 1 - my phone
PublicKey = PASTE_CLIENT1_PUBLIC_KEY_HERE
AllowedIPs = 10.66.0.2/32

Each client gets a unique IP inside the 10.66.0.0/24 tunnel subnet - the server is .1, this client is .2, your next client will be .3, and so on.

Now build the client's config file (this is what you'll import on your phone):

sudo nano /etc/wireguard/client1.conf
[Interface]
PrivateKey = PASTE_CLIENT1_PRIVATE_KEY_HERE
Address = 10.66.0.2/32
# Use 1.1.1.1 (Cloudflare) - or point this at your own Pi-hole for ad-blocked DNS
DNS = 1.1.1.1

[Peer]
PublicKey = PASTE_SERVER_PUBLIC_KEY_HERE
# Replace with your VPS's public IP and the same UDP port from Step 3
Endpoint = YOUR_SERVER_PUBLIC_IP:51820
# 0.0.0.0/0 means "route all traffic through the tunnel"
# Use 10.66.0.0/24 instead if you only want to reach the server's services
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

The PersistentKeepalive = 25 tells the client to send a keepalive packet every 25 seconds. This isn't required, but it keeps the NAT mappings on mobile carriers alive so incoming server-side traffic still reaches the client through cellular networks.

Step 6: Bring up the tunnel

On the server, start WireGuard and enable it on boot:

sudo systemctl enable --now wg-quick@wg0
sudo systemctl status wg-quick@wg0

You should see "active (exited)" - WireGuard is running. Check that the interface exists:

sudo wg show

That'll print the interface details and any connected peers. Right now there'll be a peer entry for client1 but no "latest handshake" yet (we haven't connected from the client side).

Step 7: Connect from the client

Get the client1.conf file onto your client device. Easiest way for phones is to generate a QR code on the server:

sudo apt install -y qrencode
sudo qrencode -t ansiutf8 < /etc/wireguard/client1.conf

Open the WireGuard app on your phone, hit the + button, choose "Scan from QR code," and point it at your terminal. Done. The tunnel will activate; wg show on the server will now show a "latest handshake" timestamp under that peer.

For desktop, copy the client1.conf over (scp works fine) and import it via "Add Tunnel" -> "Import from file."

Step 8: Test it actually works

From the client device, with the WireGuard tunnel active:

  • Visit icanhazip.com - you should see your VPS's public IP, not your home/ISP IP.
  • Run ping 10.66.0.1 from the client - if you've enabled ICMP on the server, this confirms the tunnel itself is up.
  • Check DNS resolution is working: open any website. If the page loads, you're done.

Common gotchas

"Handshake never completes"

Nine times out of ten this is the firewall on the VPS provider's side blocking UDP 51820. Open the port: sudo ufw allow 51820/udp. If your provider has an external network firewall (some do), open it there too. Pulsar67 doesn't block any ports by default - the firewall is yours to configure.

"The tunnel connects but the internet doesn't work"

This is almost always missing NAT or IP forwarding. Re-check Step 4 (sysctl net.ipv4.ip_forward should return 1) and confirm the eth0 name in your PostUp/PostDown rules matches your actual public interface name.

"It works on Wi-Fi but breaks on cellular"

Add PersistentKeepalive = 25 to the [Peer] section of the client config (already in the template above, but worth confirming if you reused a different config). Carriers aggressively close UDP NAT mappings; keepalives prevent it.

"My MTU is wrong"

If pages load but feel slow or some downloads stall, your tunnel MTU may need tuning. WireGuard defaults to 1420; if your underlying network has PPPoE or IPv6-over-IPv4 tunneling, you may need to drop to 1380. Add MTU = 1380 under [Interface] in wg0.conf and restart the tunnel.

Where to go next

Once the basic tunnel is up, the obvious next move is to install Pi-hole on the same VPS and point your clients' DNS at it - then your phone gets ad-free DNS on cellular too. The cost is still $3/month and the privacy gain is enormous.

Other things to add when you're comfortable:

  • Per-peer firewall rules. Restrict which internal services each peer can reach.
  • Fail2ban on the SSH port. Not WireGuard-specific, but you should always do this.
  • A second peer for your home network as a site-to-site link, so you can reach devices behind your home router from anywhere.
  • Automate client provisioning. Once you have more than two or three clients, scripts like angristan/wireguard-install save a lot of manual config editing.

If you want the marketing-page version of all this with the plan recommendations spelled out, the WireGuard hosting page covers it. If you just want to spin up a VPS and get started, plans are here.


Hit a snag? The Pulsar67 contact form goes to real engineers - we'll help you read your wg show output instead of templating a "have you tried restarting" response.

Posted by · May 26, 2026

Need a VPS for your WireGuard server?

Pulsar Nano starts at $3/month. NVMe SSD, dedicated IPv4 + IPv6, DDoS protection, online in under 60 seconds.