How to Install WireGuard VPN on Ubuntu 24.04 — Fast, Modern, Self-Hosted
A personal VPN on a VPS is one of the highest-leverage things you can do with a Linux server: secure your laptop on hotel Wi-Fi, reach a home network from the road, give a small team shared access to internal services, or simply route traffic out of a predictable IP. For years the default answer was OpenVPN, but the landscape shifted when WireGuard landed in the mainline Linux kernel in 2020. It is faster, simpler, and has been formally audited. This guide walks you through a full WireGuard server build on Ubuntu 24.04 LTS — from first SSH connection to mobile QR onboarding and multi-peer production config.
Want a VPN without the setup? The VPS-Server.host Starter plan gives you a static public IP, 2 vCPU and 4 GB RAM — plenty for a personal or small-team WireGuard server. Spin one up in 60 seconds.
Table of Contents
Why WireGuard?
WireGuard was designed from scratch by Jason A. Donenfeld to fix the two biggest problems with older VPNs: complexity and speed. The entire codebase is roughly 4,000 lines of C — two orders of magnitude smaller than OpenVPN or IPsec — which makes it far easier to audit and far less prone to bugs. Independent cryptographic reviews (including one commissioned by the French ANSSI) have found no serious issues.
On the wire it uses a fixed, modern cipher suite: ChaCha20 for symmetric encryption, Poly1305 for authentication, Curve25519 for key exchange, BLAKE2s for hashing, and SipHash24 for hashtable keys. There is no cipher negotiation, which means no downgrade attacks and no configuration surface to misconfigure.
Because WireGuard runs inside the Linux kernel, packets never copy between kernel and user space the way they do with OpenVPN. The result is dramatically higher throughput — in back-to-back tests it comfortably pushes 1 Gbps on modest hardware where OpenVPN tops out around 200-300 Mbps. Handshakes complete in about 1 RTT, so roaming between networks is nearly instant.
The configuration model matches the simplicity of the protocol. Instead of a sprawling server.conf with dozens of directives, you get one ini-style file per interface: an [Interface] block for your side, and one [Peer] block per remote endpoint. That is the whole mental model.
If you are coming from OpenVPN or wrestling with IPsec, the difference is stark — and if you are considering a fully managed mesh VPN, take a look at Tailscale, NetBird or Headscale later in this guide.
Prerequisites
Before starting, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access
- SSH access to the server (built-in terminal on macOS/Linux, or PuTTY/Windows Terminal on Windows)
- A static public IPv4 address on the server (every VPS-Server.host plan includes one)
- A client device — desktop (Linux/macOS/Windows) or mobile (iOS/Android)
Recommended Plan: Starter>
WireGuard is extremely lightweight, so you do not need a large server. Our Starter plan is a great fit:>
- 2 vCPU cores
- 4 GB RAM
- 50 GB NVMe SSD
- 32 TB monthly traffic
- Dedicated static IPv4>
This easily supports 50-100 concurrent VPN users for everyday browsing. For heavier site-to-site workloads, step up to a 4-6 vCPU plan.
Connect to your server:
ssh root@your-server-ipStep 1: Update System Packages
Start with a clean, patched base. WireGuard has been in the mainline kernel since 5.6, so Ubuntu 24.04 (kernel 6.8+) ships everything you need — but you still want the latest security updates.
sudo apt update && sudo apt upgrade -yIf the kernel was upgraded, reboot before continuing:
sudo rebootReconnect via SSH after a minute.
Step 2: Install WireGuard
The wireguard package on Ubuntu is a meta-package that pulls in the userspace tools (wg, wg-quick) and the kernel module. Since 24.04 ships with a recent kernel, the module is already available — no DKMS build required.
sudo apt install -y wireguard wireguard-tools qrencodeWe add qrencode now so we can later render client configs as QR codes for mobile onboarding.
Verify the install:
wg --versionExpected output:
wireguard-tools v1.0.20210914 - https://git.zx2c4.com/wireguard-tools/Confirm the kernel module loads:
sudo modprobe wireguard && lsmod | grep wireguardExpected output:
wireguard 212992 0
curve25519_x86_64 36864 1 wireguard
libchacha20poly1305 16384 1 wireguard
ip6_udp_tunnel 16384 1 wireguard
udp_tunnel 32768 1 wireguardStep 3: Generate the Server Keypair
Every WireGuard peer — server or client — is identified by a Curve25519 keypair. There is no certificate authority, no CSRs, no expiry dates. You generate the private key, derive the public key, and share only the public key with the other side.
Move into the WireGuard config directory and tighten permissions up front:
cd /etc/wireguard
sudo umask 077Generate the server keypair with a single pipeline:
wg genkey | sudo tee /etc/wireguard/privatekey | wg pubkey | sudo tee /etc/wireguard/publickeyThis produces two files:
/etc/wireguard/privatekey— the server's private key (keep secret)/etc/wireguard/publickey— the server's public key (shared with clients)
sudo cat /etc/wireguard/privatekey
sudo cat /etc/wireguard/publickeyExpected output (your keys will differ):
kJ8+7BqH4zvL9mN3pQ2rS5tU6wX8yA1bC4dE7fG0hJk=
H3xL9mN5pQ2rS8tU6wX4yA7bC0dE1fG2hJ8kM3nP6qR=Both keys are base64-encoded, 44 characters long, and end with =. If yours look different, regenerate them.
Step 4: Create the Server Config (wg0.conf)
The server config lives at /etc/wireguard/wg0.conf. The interface name wg0 is convention — you can have wg1, wg2 etc. for separate tunnels.
First, grab the server's primary network interface name — you will need it for the NAT rules:
ip route | grep defaultExpected output:
default via 203.0.113.1 dev eth0 proto staticIn this example the interface is eth0. On some cloud VPS images it may be ens3, ens18 or enp1s0. Use whatever appears after dev.
Now create the config:
sudo nano /etc/wireguard/wg0.confPaste the following, substituting your server's private key and interface name:
[Interface]Tunnel IP for the server inside the VPN
Address = 10.8.0.1/24UDP port WireGuard listens on
ListenPort = 51820Server private key (from /etc/wireguard/privatekey)
PrivateKey = kJ8+7BqH4zvL9mN3pQ2rS5tU6wX8yA1bC4dE7fG0hJk=Save peer state back to the file when the service stops
SaveConfig = falseNAT masquerade so clients can reach the public internet via eth0
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT
--- Peers are appended below, one [Peer] block per client ---
A quick walk-through:
Address = 10.8.0.1/24— the server's address inside the tunnel. The/24tells WireGuard the VPN subnet is10.8.0.0/24, giving you 254 usable peer IPs. Pick any RFC 1918 subnet that does not conflict with your home or office LAN (common choices:10.8.0.0/24,10.10.10.0/24,172.16.100.0/24).ListenPort = 51820— the default UDP port. You can change this, but keeping it default is fine since only peers with the right key can even get a response.PrivateKey— paste the exact contents of/etc/wireguard/privatekey.SaveConfig = false— keeps the config file stable so git tracking, Ansible etc. work cleanly. If you set this totrue,wg-quick downwill rewrite the file with any runtime changes, which is surprising.PostUp/PostDown— shell commands run after the interface comes up or before it goes down. These add/remove the NAT and forwarding rules we need to turn the VPS into an internet gateway for VPN clients.
Ctrl+O, Enter, Ctrl+X).Lock down the file permissions:
sudo chmod 600 /etc/wireguard/wg0.conf /etc/wireguard/privatekeyStep 5: Enable IP Forwarding
By default, Linux does not forward packets between interfaces. Without this, traffic arriving on wg0 will never be routed out to eth0 — clients will connect but reach nothing.
Enable IPv4 forwarding persistently:
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-wireguard.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-wireguard.conf
sudo sysctl --systemExpected output (abbreviated):
* Applying /etc/sysctl.d/99-wireguard.conf ...
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1Verify the value is live:
sysctl net.ipv4.ip_forwardExpected output:
net.ipv4.ip_forward = 1Step 6: Configure NAT Masquerade
The PostUp rules in wg0.conf already handle NAT via iptables, and Ubuntu 24.04's iptables-nft shim transparently translates those calls into nftables rules. If you prefer native nftables for clarity, create a persistent ruleset instead:
sudo nano /etc/nftables.confAppend (or create) the following:
table inet wireguard_nat { chain postrouting { type nat hook postrouting priority srcnat; policy accept; oifname "eth0" ip saddr 10.8.0.0/24 masquerade }
chain forward { type filter hook forward priority filter; policy accept; iifname "wg0" accept oifname "wg0" accept } }
Enable and apply:
sudo systemctl enable --now nftables
sudo nft -f /etc/nftables.conf
sudo nft list rulesetIf you go the nftables route, remove the PostUp/PostDown lines from wg0.conf so you are not double-applying rules. For most users the default iptables-in-wg0.conf approach is simpler and works out of the box — only switch if you are already managing nftables elsewhere.
Step 7: Open UDP 51820 in UFW
Ubuntu's UFW makes firewall management painless. Allow SSH (so you do not lock yourself out), allow the WireGuard port, then enable the firewall.
sudo ufw allow 22/tcp
sudo ufw allow 51820/udp
sudo ufw enable
sudo ufw status verboseExpected output:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
51820/udp ALLOW IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)
51820/udp (v6) ALLOW IN Anywhere (v6)Important: UFW forwarding policy
UFW defaults to DROP on the FORWARD chain, which will silently kill client traffic even with forwarding enabled in sysctl. Fix it:
sudo sed -i 's/^DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufw
sudo ufw reloadStep 8: Start wg0 and Enable at Boot
Bring the tunnel up for the first time:
sudo wg-quick up wg0Expected output:
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.8.0.1/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPTConfirm the interface is live:
sudo wg showExpected output:
interface: wg0
public key: H3xL9mN5pQ2rS8tU6wX4yA7bC0dE1fG2hJ8kM3nP6qR=
private key: (hidden)
listening port: 51820No peers yet — we will add them in Step 9.
Enable WireGuard at boot via the bundled systemd unit:
sudo systemctl enable wg-quick@wg0
sudo systemctl status wg-quick@wg0Expected output:
● [email protected] - WireGuard via wg-quick(8) for wg0
Loaded: loaded (/usr/lib/systemd/system/[email protected]; enabled; preset: enabled)
Active: active (exited) since Wed 2026-04-16 10:15:00 UTC; 30s agoThe wg-quick@ template unit reads /etc/wireguard/wg0.conf on boot and runs the same wg-quick up wg0 command you just ran manually.
Step 9: Generate Client Configs
Each client gets its own keypair and its own entry in the VPN subnet. Generate a keypair for the first client (we will call it laptop):
cd /etc/wireguard
mkdir -p clients && cd clients
wg genkey | tee laptop.key | wg pubkey > laptop.pub
chmod 600 laptop.keyView the keys:
cat laptop.key
cat laptop.pubBuild the client config
Create laptop.conf:
sudo nano /etc/wireguard/clients/laptop.confPaste (substituting placeholders):
[Interface]
Client private key (from laptop.key)
PrivateKey = <CLIENT_PRIVATE_KEY>
Client VPN IP — must be unique within 10.8.0.0/24
Address = 10.8.0.2/32
DNS servers used while the tunnel is up (Cloudflare + Google here; use your own if preferred)
DNS = 1.1.1.1, 8.8.8.8[Peer]
Server public key (from /etc/wireguard/publickey)
PublicKey = <SERVER_PUBLIC_KEY>
Full-tunnel: send all traffic through the VPN
AllowedIPs = 0.0.0.0/0, ::/0
Public IP:port of the server
Endpoint = your-server-ip:51820
Keep NAT mappings open on mobile networks (every 25s)
PersistentKeepalive = 25Register the peer on the server
Append a [Peer] block to /etc/wireguard/wg0.conf:
sudo nano /etc/wireguard/wg0.confAdd at the bottom:
[Peer]
laptop
PublicKey = <CLIENT_PUBLIC_KEY>
AllowedIPs = 10.8.0.2/32Save and exit. Reload the config without disrupting the interface:
sudo wg syncconf wg0 <(wg-quick strip wg0)Verify the peer is registered:
sudo wg showExpected output:
interface: wg0 public key: H3xL9mN5pQ2rS8tU6wX4yA7bC0dE1fG2hJ8kM3nP6qR= private key: (hidden) listening port: 51820
peer: <CLIENT_PUBLIC_KEY> allowed ips: 10.8.0.2/32
The peer shows no handshake yet — that will happen once the client connects.
Step 10: Connect a Mobile Device with a QR Code
Typing a 44-character base64 key into a phone by hand is miserable. The official WireGuard mobile apps (iOS and Android) can scan a QR code containing the entire client config.
On the server, render laptop.conf as a QR code in the terminal:
sudo qrencode -t ansiutf8 < /etc/wireguard/clients/laptop.confA block-art QR code will appear. Open the WireGuard app on your phone, tap Add Tunnel → Create from QR code, and scan the terminal.
Toggle the tunnel on. Within a second or two, sudo wg show on the server should display a fresh latest handshake timestamp and non-zero transfer bytes:
peer: <CLIENT_PUBLIC_KEY>
endpoint: 198.51.100.42:53471
allowed ips: 10.8.0.2/32
latest handshake: 3 seconds ago
transfer: 1.24 KiB received, 892 B sentTest that traffic is flowing out through the VPS: on the phone, visit https://ifconfig.me — it should show your server's IP, not the phone's cellular or Wi-Fi IP.
For desktop clients, copy /etc/wireguard/clients/laptop.conf to the device (over SSH with scp, never email or chat) and import it through the WireGuard GUI or place it in /etc/wireguard/ on Linux and run sudo wg-quick up laptop.
Split-Tunnel vs Full-Tunnel
The single most impactful setting on the client is AllowedIPs. It serves double duty: on the wire it tells WireGuard which remote subnets the peer is authorised to send packets into, and locally it also installs routes telling the OS which outbound traffic to send through the tunnel.
Full-tunnel (privacy VPN)
AllowedIPs = 0.0.0.0/0, ::/0Every packet leaves the device through the VPN. Use this when you want the VPS to act as your public internet egress — hotel Wi-Fi, public cafes, geo-unblocking, or simply masking your home IP.
Split-tunnel (private resource access)
AllowedIPs = 10.8.0.0/24, 192.168.50.0/24Only traffic destined for the VPN subnet (10.8.0.0/24) and your home LAN (192.168.50.0/24) goes through the tunnel. Netflix, YouTube, local printers — everything else — uses the client's normal internet connection. This is ideal for company VPNs, where you want employees to reach internal services without routing their entire browsing history through your server.
Mix and match
You can add specific public IPs or CIDRs to a split-tunnel config to selectively route certain destinations. For example, tunneling only traffic to a customer's IP range:
AllowedIPs = 10.8.0.0/24, 203.0.113.0/24Remember: if you change AllowedIPs you usually do not need to touch the server config. The server's [Peer] AllowedIPs acts as a cryptographic access control list (which source IPs this peer is allowed to claim), while the client's AllowedIPs is a routing directive.
Adding Multiple Peers
Scaling from one user to many is a matter of repeating Step 9. Here is a clean, scriptable pattern.
Create a helper script at /usr/local/sbin/wg-add-peer:
sudo nano /usr/local/sbin/wg-add-peer#!/usr/bin/env bash set -euo pipefailNAME="${1:-}" if [[ -z "$NAME" ]]; then echo "Usage: wg-add-peer <name>" exit 1 fi
SERVER_PUB=$(cat /etc/wireguard/publickey) SERVER_ENDPOINT="${WG_ENDPOINT:-your-server-ip}:51820" CLIENT_DIR="/etc/wireguard/clients" mkdir -p "$CLIENT_DIR"
Find next free IP in 10.8.0.0/24 (starts at .2, .1 is server)
USED=$(grep -Eo 'AllowedIPs = 10\.8\.0\.[0-9]+' /etc/wireguard/wg0.conf | grep -Eo '[0-9]+$' | sort -n || true) NEXT=2 for ip in $USED; do if [[ "$ip" -ge "$NEXT" ]]; then NEXT=$((ip + 1)); fi done CLIENT_IP="10.8.0.$NEXT"umask 077 wg genkey | tee "$CLIENT_DIR/$NAME.key" | wg pubkey > "$CLIENT_DIR/$NAME.pub" PRIV=$(cat "$CLIENT_DIR/$NAME.key") PUB=$(cat "$CLIENT_DIR/$NAME.pub")
cat > "$CLIENT_DIR/$NAME.conf" <<EOF [Interface] PrivateKey = $PRIV Address = $CLIENT_IP/32 DNS = 1.1.1.1, 8.8.8.8
[Peer] PublicKey = $SERVER_PUB AllowedIPs = 0.0.0.0/0, ::/0 Endpoint = $SERVER_ENDPOINT PersistentKeepalive = 25 EOF
cat >> /etc/wireguard/wg0.conf <<EOF
[Peer]
$NAME
PublicKey = $PUB AllowedIPs = $CLIENT_IP/32 EOFwg syncconf wg0 <(wg-quick strip wg0)
echo "Peer '$NAME' added with IP $CLIENT_IP" echo "Config: $CLIENT_DIR/$NAME.conf" echo qrencode -t ansiutf8 < "$CLIENT_DIR/$NAME.conf"
Make it executable and use it:
sudo chmod +x /usr/local/sbin/wg-add-peer
sudo WG_ENDPOINT=your-server-ip wg-add-peer phone
sudo WG_ENDPOINT=your-server-ip wg-add-peer work-laptop
sudo WG_ENDPOINT=your-server-ip wg-add-peer partner-macEach invocation picks the next free tunnel IP, generates keys, writes a client config, appends a [Peer] block to the server config, and prints a QR code — all atomically, with no hand-editing.
wg-easy: A Web UI Alternative
If hand-editing ini files is not your style, wg-easy wraps everything in this guide behind a clean browser UI. It runs as a single Docker container, exposes an admin dashboard on port 51821, and handles peer creation, QR generation, config downloads, traffic graphs and enable/disable toggles with a few clicks.
A minimal deploy:
docker run -d \
--name wg-easy \
--cap-add=NET_ADMIN --cap-add=SYS_MODULE \
--sysctl="net.ipv4.conf.all.src_valid_mark=1" \
--sysctl="net.ipv4.ip_forward=1" \
-e WG_HOST=your-server-ip \
-e PASSWORD_HASH='<bcrypt-hash>' \
-p 51820:51820/udp \
-p 51821:51821/tcp \
-v ~/.wg-easy:/etc/wireguard \
--restart unless-stopped \
ghcr.io/wg-easy/wg-easyUnder the hood it still writes a wg0.conf compatible with everything in this guide, so you can migrate in or out of wg-easy without reinstalling WireGuard. It is a great choice when onboarding non-technical team members, and it pairs well with a reverse proxy + basic auth on the admin port.
For a full mesh experience with identity provider integration, ACLs and NAT traversal, look at the Tailscale, NetBird or self-hosted Headscale guides — all three are built on top of the WireGuard protocol.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
Handshake never completes (latest handshake stays empty) | UDP 51820 blocked upstream or wrong endpoint | Verify with sudo ss -ulnp \</td><td>grep 51820<code> on the server and </code>nc -u -v your-server-ip 51820 from the client. Check UFW and any cloud firewall in the provider console. |
| Handshake succeeds but no internet on the client | IP forwarding or NAT misconfigured | sysctl net.ipv4.ip_forward must be 1. Check sudo iptables -t nat -L POSTROUTING -n -v shows the MASQUERADE rule. Confirm DEFAULT_FORWARD_POLICY=ACCEPT in /etc/default/ufw. |
| Websites load slowly / images broken | MTU mismatch | Set MTU = 1380 in the client [Interface] block. WireGuard defaults to 1420 but some ISPs drop fragmented packets. |
DNS leaks (client uses local DNS instead of 1.1.1.1) | Client DNS not applied | On Linux, install resolvconf or openresolv. On Windows, the official WireGuard app handles this automatically. |
RTNETLINK answers: Operation not supported on wg-quick up | Kernel module missing | Run sudo modprobe wireguard. If it fails, reboot after apt upgrade — you likely pulled a kernel update without rebooting. |
| Two peers cannot reach each other (peer-to-peer within the VPN) | Missing routes on peers | Each client's AllowedIPs must include the VPN subnet (e.g. 10.8.0.0/24), not just 10.8.0.1/32. |
| Second peer using duplicate IP silently breaks both | AllowedIPs in server config overlaps | Every [Peer] AllowedIPs entry on the server must be unique. Use /32 per peer. |
Live debugging
Stream the WireGuard handshake state in real time:
watch -n 1 'sudo wg show'Check kernel messages for dropped packets:
sudo journalctl -k --since "5 minutes ago" | grep -iE 'wireguard|wg0'FAQ
Is WireGuard really faster than OpenVPN?
Yes, by a wide margin. WireGuard lives in the Linux kernel and uses a fixed, modern cipher suite (ChaCha20, Curve25519, BLAKE2s), so there is no per-packet context switch between kernel and userspace. Back-to-back benchmarks on identical hardware typically show WireGuard pushing 3-4x the throughput of OpenVPN and handshakes completing in roughly one round-trip. The entire codebase is also ~4,000 lines vs. OpenVPN's 70,000+, which makes audits tractable — WireGuard has passed formal reviews including one commissioned by the French cybersecurity agency ANSSI.
Do I need a static IP on my VPS for WireGuard?
A static public IP is strongly recommended because the Endpoint in every client config points at a fixed IP (or DNS name). If the server IP changes you have to re-issue every client config. Every VPS-Server.host plan includes a dedicated static IPv4 by default, so you do not need to configure anything extra. If you use a dynamic IP, point a dynamic DNS name at it and use the DNS name as the endpoint.
What is the difference between split-tunnel and full-tunnel WireGuard?
It is entirely controlled by the AllowedIPs line in the client config. Full-tunnel (0.0.0.0/0, ::/0) sends every packet the device generates through the VPN — useful for public Wi-Fi, geo-unblocking, or making your client appear at the VPS IP. Split-tunnel lists only specific subnets (say 10.8.0.0/24, 192.168.1.0/24), so only traffic matching those routes is tunneled and general internet usage stays on the local connection. Split-tunnel is faster and saves VPS bandwidth; full-tunnel is safer on untrusted networks.
How many peers can a single WireGuard server handle?
WireGuard itself scales to thousands of peers on commodity hardware — the protocol was designed for this. In practice the bottleneck is almost always bandwidth, not CPU. On a 2 vCPU Starter VPS with a 1 Gbps port you can comfortably support 50-100 concurrent users for general browsing, or 20-30 for heavy streaming. For 500+ peers or a high-throughput corporate deployment, step up to 4-6 vCPU, tune net.core.rmem_max / wmem_max, and consider multiple wgN interfaces bound to separate CPU cores.
Should I use iptables or nftables for NAT with WireGuard?
Either works. Ubuntu 24.04 ships nftables as the default backend, and the iptables-nft compatibility layer translates legacy iptables commands to nftables rules on the fly. The PostUp/PostDown lines shown in this guide take advantage of that — simple and reliable. For new deployments where you are already writing nftables rules for other services, the native nftables table shown in Step 6 is cleaner. Just do not mix both at once, or you will end up with duplicate rules.
Is wg-easy a good alternative to editing wg0.conf by hand?
Yes, especially for teams. wg-easy is a lightweight Docker-based web UI that handles peer creation, QR code generation, enable/disable toggles and traffic graphs without touching the terminal. Under the hood it still writes a standard wg0.conf, so you can migrate back to the CLI workflow from this guide at any time without re-provisioning clients. It is ideal when you need to onboard less-technical users or want a dashboard view of who is connected.
How does WireGuard compare to Tailscale and NetBird?
Tailscale and NetBird are overlay mesh networks built on top of the WireGuard protocol. They add automatic peer discovery, NAT traversal (so peers behind CGNAT can reach each other), identity-based ACLs, and a managed coordination server that distributes keys. Vanilla WireGuard — what this guide covers — is lower-level: you run one hub server, you control every peer, and there is no external dependency. Choose Tailscale or NetBird when you want zero-config mesh networking for a team. Choose self-hosted Headscale if you like Tailscale's model but want to run the coordination server yourself. Choose vanilla WireGuard when you want a minimal, auditable, single-server setup with no external control plane.
Next Steps
You now have a production-quality WireGuard server. Here is where to go from here:
- Harden SSH on the server — once the VPN is up, you can move SSH to listen only on
10.8.0.1and firewall off port 22 from the public internet entirely. - Monitor with Prometheus —
prometheus-wireguard-exporterscrapeswg show dumpand gives you per-peer bandwidth graphs in Grafana. - Add a kill switch on clients — set
PostUp/PostDownon the client interface to block all non-tunnel traffic so a VPN drop fails closed. - Try a mesh overlay — see our guides on Tailscale, NetBird and Headscale for zero-config peer-to-peer networking.
- Compare with OpenVPN — if you are migrating from a legacy setup, our OpenVPN guide walks through the same scenarios so you can benchmark side by side.
- Read the official docs — the wireguard.com/install/ page lists platform-specific installers and links to the full protocol spec for deeper study.
Deploy Your WireGuard VPS in 60 Seconds>
The VPS-Server.host Starter plan is purpose-built for personal and small-team VPN workloads:>
- 2 vCPU, 4 GB RAM, 50 GB NVMe
- Dedicated static IPv4 included
- 32 TB monthly traffic on a 1 Gbps port
- Ubuntu 24.04 LTS image ready in under a minute>
Launch a Starter VPS now and follow this guide end to end in about 25 minutes.