How to Set Up a WireGuard VPN Server on a VPS
Roll your own private VPN with WireGuard — fast, modern and easy to manage. This guide uses a web UI so you add devices by scanning a QR code.

WireGuard is a lean, fast VPN protocol that's now part of the Linux kernel. Running your own on a VPS gives you an encrypted tunnel for public Wi-Fi, a stable IP for whitelisting, and private access to other services you self-host. This guide uses wg-easy, a Docker-based web UI, so adding a phone is as simple as scanning a QR code. Prefer the pure command-line route? The classic manual setup lives in how to set up a WireGuard VPN on a VPS.
Why WireGuard over OpenVPN?
WireGuard is faster, has a tiny codebase that's easier to audit, and reconnects seamlessly when you switch networks. OpenVPN is more configurable and better at slipping through restrictive firewalls on TCP 443. If that's your situation, see how to set up an OpenVPN server.
Prerequisites
- A VPS with Docker installed — how to run Docker on a VPS covers it.
- Your server's public IP or a domain pointing at it.
Deploy wg-easy
mkdir -p ~/wireguard && cd ~/wireguard
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:latest
container_name: wg-easy
restart: unless-stopped
environment:
- WG_HOST=vpn.example.com
- PASSWORD_HASH=replace-with-bcrypt-hash
- WG_DEFAULT_DNS=1.1.1.1
volumes:
- ./config:/etc/wireguard
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
Set WG_HOST to your server's public address. Generate the admin password hash and start it:
docker compose up -d
The web UI is at http://your-server-ip:51821.

Open the firewall
WireGuard uses UDP 51820. Keep the admin panel (51821) restricted to your IP:
sudo ufw allow OpenSSH
sudo ufw allow 51820/udp
sudo ufw allow from YOUR.IP.ADDR to any port 51821 proto tcp
sudo ufw enable
Add a device
In the web UI, click New Client, give it a name, and a QR code appears. Install the WireGuard app on your phone, tap + → Scan from QR code, and you're connected. For laptops, download the config file and import it into the desktop WireGuard client.
Use it as a private gateway to other services
This is where self-hosting gets powerful. Once your devices are on the VPN, you can keep services like Pi-hole or an internal dashboard bound to the VPN interface only — never exposed to the public internet. Point clients at the server's internal VPN IP and those services become private-by-default.
Verify the tunnel
On a connected client, check your public IP — it should now be the VPS. On the server:
docker compose exec wg-easy wg show
That lists each peer and the last handshake time, confirming traffic is flowing.
Full tunnel vs split tunnel
By default wg-easy routes all your device's traffic through the VPS (a full tunnel) — ideal on untrusted Wi-Fi because everything is encrypted to the server. But sometimes you only want to reach your private services, not route your whole connection abroad. That's a split tunnel, controlled by the client's AllowedIPs setting:
- Full tunnel:
AllowedIPs = 0.0.0.0/0, ::/0— every packet goes through the VPN. - Split tunnel:
AllowedIPs = 10.8.0.0/24— only traffic to the VPN subnet (your other self-hosted apps) uses the tunnel; normal browsing goes direct.
Split tunnels are perfect when you just want private access to a dashboard or Pi-hole without the latency of routing everything through the server.
Keeping the tunnel healthy
On mobile networks that drop idle connections, add a keepalive so the tunnel reconnects instantly. In the client config set:
PersistentKeepalive = 25
That sends a tiny packet every 25 seconds to hold the connection open. If a client ever fails to connect, check three things in order: the UDP port is open on the firewall, the server's WG_HOST matches its real public address, and the device's clock is correct — WireGuard is time-sensitive and a skewed clock breaks the handshake.
Adding devices and managing keys
Each device gets its own key pair and its own client entry — never share one config across devices. In the wg-easy UI, click New Client per phone, laptop or tablet; each appears as a separate peer you can enable, disable or delete independently. That per-device model is a security feature: if a phone is lost, delete just that client and its access is revoked instantly, without disturbing anyone else.
For a headless device like a server or a Raspberry Pi, download its config file and drop it in /etc/wireguard/wg0.conf, then bring the tunnel up with the kernel tools:
sudo apt install wireguard
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0
That last line makes the tunnel reconnect automatically on boot — ideal for always-on devices that should stay on the VPN. Keep the wg-easy admin panel (port 51821) restricted to your own IP or reachable only over an existing connection, since it can generate new access to your network.
FAQ
Is WireGuard faster than OpenVPN?
Generally yes. Its smaller codebase and kernel integration mean lower overhead and higher throughput, especially on mobile.
Can one WireGuard server handle many devices?
Easily. A modest VPS handles dozens of peers; the limiting factor is your bandwidth allowance, not WireGuard itself.
Does a self-hosted VPN make me anonymous?
No. It hides your traffic from the local network and gives you the VPS's IP, but the VPS provider and destinations can still see traffic. It's for privacy and security on untrusted networks, not anonymity.
What port does WireGuard use?
UDP 51820 by default. You can change it, but UDP is required — WireGuard doesn't run over TCP.
Want a low-latency endpoint of your own? Deploy this on a VPN server VPS with full root access, and browse the VPS plans to pick a region close to you.