Launch pricing: every plan costs 30% less than the cheapest offshore competitor we track. See the benchmarkEvery plan 30% under the cheapest offshore host

Knowledge base · VPS

How to host your own VPN on a VPS with WireGuard

Host a VPN on a VPS with WireGuard: install it on Ubuntu 24.04 or Debian, write wg0.conf with NAT, open UFW, add clients by QR code and test for leaks.

Updated 11 min read

In this guide

  • WireGuard runs inside the Linux kernel, so the smallest VPS plan is enough for a personal VPN.
  • Create keys with umask 077, then write /etc/wireguard/wg0.conf with NAT on your real network interface.
  • Turn on IP forwarding, allow 51820/udp and the tunnel's route in UFW, and keep SSH open first.
  • Give every device its own key pair and address; phones scan their configuration as a QR code.
  • Check your public IP, DNS and IPv6 from a connected device before you rely on the tunnel.
VPS13 sections
All guides
On this page
  1. Before you start: what your WireGuard VPS needs
  2. Install WireGuard on Ubuntu or Debian
  3. Generate the server and client keys
  4. Write the WireGuard server configuration
  5. Enable IP forwarding
  6. Open the firewall
  7. Start the tunnel and enable it at boot
  8. Add a client
  9. Add IPv6 (optional)
  10. Check for leaks
  11. Keep your WireGuard VPS updated and add more clients
  12. Troubleshooting
  13. Frequently asked questions

To host your own VPN on a VPS with WireGuard, install the wireguard package, create key pairs for the server and each device, describe the tunnel in /etc/wireguard/wg0.conf with NAT to the server's network interface, turn on IP forwarding, open UDP port 51820 and start wg-quick@wg0. Each device connects with its own configuration file or QR code.

These steps set up a WireGuard VPS on an Offshore VPS running Ubuntu 24.04 LTS, Debian 12 or Debian 13, with root access over SSH as described in getting started. Replace SERVER_IP with your server's IPv4 address and eth0 with your network interface.

Before you start: what your WireGuard VPS needs

  • A VPS with a public IPv4 address. Every plan includes a dedicated IPv4 address and a /64 IPv6 block.
  • Root access over SSH.
  • The WireGuard app on each device. The WireGuard install page lists official apps for Windows, macOS, iOS and Android; Linux computers use the wireguard-tools package.

The smallest plan is enough. WireGuard runs inside the Linux kernel and needs little memory, so the Dinghy plan (1 vCPU, 1 GB RAM, $2.39 a month) carries a personal VPN. Encryption is CPU work: pick more vCPUs if many devices will move large amounts of data at once. Bandwidth is unmetered under fair use.

A location close to you keeps browsing fast; one in another country changes which law applies to the server. Compare latency on the network page and read our guide to an offshore VPS for a VPN before you choose. As for any no-KYC VPS, your account is an email address and a password, and you pay in crypto.

Our acceptable use policy lists VPNs among the welcome uses. Your devices' traffic leaves with your server's IP address, so abuse sent through the tunnel counts as coming from your server: give access only to people you trust. Outbound port 25 is closed by default.

Install WireGuard on Ubuntu or Debian

To set up WireGuard on Ubuntu 24.04 or Debian, install three packages and check the version:

apt update
apt install -y wireguard ufw qrencode
wg --version

The wireguard package installs the wg and wg-quick tools. The tunnel itself is part of the kernel on Ubuntu 24.04 and Debian 12 and 13, so nothing needs compiling. ufw brings the iptables command that the NAT rules use; leave it out if you manage your firewall with nftables. qrencode draws QR codes for phones.

Generate the server and client keys

cd /etc/wireguard
umask 077
wg genkey | tee server.key | wg pubkey > server.pub
wg genkey | tee laptop.key | wg pubkey > laptop.pub

umask 077 makes the files you create in this shell readable by root only, and /etc/wireguard is already closed to other users. wg genkey prints a random private key and wg pubkey derives its public key, as in the WireGuard quick start. Whoever holds a private key can connect as that device, so keep it secret.

Use one key pair per device, named after it, so you can revoke one device without touching the others. You can also generate a device's keys on the device itself and copy only its public key to the server.

Write the WireGuard server configuration

The whole WireGuard server setup lives in one file. First find the interface that carries the server's internet traffic:

ip route show default

The word after dev is its name, such as eth0, ens3 or enp1s0: use it in both NAT lines. The $(cat ...) parts copy the keys into the file:

cat > /etc/wireguard/wg0.conf <<EOF
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = $(cat /etc/wireguard/server.key)
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

[Peer]
# laptop
PublicKey = $(cat /etc/wireguard/laptop.pub)
AllowedIPs = 10.8.0.2/32
EOF
chmod 600 /etc/wireguard/wg0.conf
Address
The server's address inside the tunnel. The private range 10.8.0.0/24 leaves room for 253 devices.
ListenPort
The UDP port that devices connect to; 51820 is the one in WireGuard's own examples.
PrivateKey
The server's private key, which is why the file stays readable by root only.
PostUp and PostDown
Commands that wg-quick runs with bash when the tunnel starts and stops, as its manual page describes. Here they add and remove NAT, so devices go online with the server's IPv4 address.
[Peer]
One block per device. WireGuard drops any packet from a device whose source address is outside its AllowedIPs, a design it calls cryptokey routing.

Enable IP forwarding

Linux passes packets between interfaces only when forwarding is on, and the kernel documentation lists it as off by default. Turn it on now and at every boot:

echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-wireguard.conf
sysctl --system
sysctl net.ipv4.ip_forward

The last command must print net.ipv4.ip_forward = 1. sysctl --system loads the files in /etc/sysctl.d and the other locations listed in its manual page, and the same files apply again at each boot.

Open the firewall

Allow SSH first, so that enabling UFW cannot cut you off, then the WireGuard port and the tunnel's route out:

ufw allow 22/tcp
ufw allow 51820/udp
ufw route allow in on wg0 out on eth0
ufw enable
ufw status verbose

Allow your own SSH port instead of 22 if you changed it with the hardening guide. ufw enable warns that it may disrupt existing SSH connections; answer y. UFW drops forwarded traffic by default, and its status shows deny (routed), so the route rule is what lets tunnel traffic out through eth0. Replies return automatically, and each rule also covers IPv6.

With nftables instead of UFW

If you keep your own /etc/nftables.conf from the hardening guide, skip UFW, delete the PostUp and PostDown lines from wg0.conf, add udp dport 51820 accept to the input chain, and add this table at the end of the file:

table inet wg-nat {
  chain postrouting {
    type nat hook postrouting priority srcnat;
    ip saddr 10.8.0.0/24 oifname "eth0" masquerade
  }
}

Check the file, then load it:

nft -c -f /etc/nftables.conf
nft -f /etc/nftables.conf

Written in the file, the NAT rule survives reloads. Added by PostUp, it would vanish whenever the file's flush ruleset line runs.

Start the tunnel and enable it at boot

systemctl enable --now wg-quick@wg0
wg show

wg-quick@wg0 reads wg0.conf, creates the wg0 interface, runs the PostUp commands and starts again at every boot. wg show lists listening port: 51820 and your laptop as a peer, without a handshake until the laptop connects. If the service fails, read its log with journalctl -u wg-quick@wg0 -b.

Add a client

A laptop or desktop: a configuration file

Write the laptop's file on the server, with your server's IPv4 address in place of SERVER_IP:

umask 077
cat > /etc/wireguard/laptop.conf <<EOF
[Interface]
PrivateKey = $(cat /etc/wireguard/laptop.key)
Address = 10.8.0.2/32
DNS = 9.9.9.9

[Peer]
PublicKey = $(cat /etc/wireguard/server.pub)
Endpoint = SERVER_IP:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
EOF
AllowedIPs = 0.0.0.0/0, ::/0
All traffic goes through the tunnel. Keep ::/0 even without IPv6 on the server: IPv6 then enters the tunnel and is dropped there, instead of leaking around it.
DNS
9.9.9.9 is Quad9, which blocks malware domains and validates DNSSEC; any resolver you trust works. Queries travel inside the tunnel.
PersistentKeepalive = 25
Stops home routers and mobile networks from closing the connection. WireGuard's quick start calls 25 seconds "a sensible interval that works with a wide variety of firewalls".

Copy the file from the laptop (if root logins are off after hardening, copy it through your sudo user), then delete the laptop's private key and file from the server, which needs only the public key:

scp root@SERVER_IP:/etc/wireguard/laptop.conf .
rm /etc/wireguard/laptop.key /etc/wireguard/laptop.conf

On Windows and macOS, import the file in the WireGuard app. On Linux, copy it to /etc/wireguard/ and run wg-quick up laptop; the DNS line needs resolvconf, which systemd-resolved provides on Ubuntu and the openresolv package on Debian.

A phone: scan a QR code

A phone needs its own keys, address and [Peer] block on the server:

cd /etc/wireguard
umask 077
wg genkey | tee phone.key | wg pubkey > phone.pub
cat >> /etc/wireguard/wg0.conf <<EOF

[Peer]
# phone
PublicKey = $(cat /etc/wireguard/phone.pub)
AllowedIPs = 10.8.0.3/32
EOF
systemctl reload wg-quick@wg0

Write phone.conf like laptop.conf, with phone.key and 10.8.0.3/32, and display it in the terminal:

qrencode -t ansiutf8 < /etc/wireguard/phone.conf

Scan it with the WireGuard app's QR code option, then delete phone.key and phone.conf from the server.

Add IPv6 (optional)

The simplest way to give devices IPv6 is a private range inside the tunnel, translated to the server's public IPv6 address as with IPv4; devices then share that address. Private ranges start with fd followed by random digits (RFC 4193), so choose your own instead of fd8c:5e2a:91b4::/64. Stop the tunnel with systemctl stop wg-quick@wg0, then in wg0.conf replace the Address line and add the IPv6 NAT lines below the IPv4 ones:

Address = 10.8.0.1/24, fd8c:5e2a:91b4::1/64
PostUp = ip6tables -t nat -A POSTROUTING -s fd8c:5e2a:91b4::/64 -o eth0 -j MASQUERADE
PostDown = ip6tables -t nat -D POSTROUTING -s fd8c:5e2a:91b4::/64 -o eth0 -j MASQUERADE

Give each device an IPv6 address on both sides, for example AllowedIPs = 10.8.0.2/32, fd8c:5e2a:91b4::2/128 in its [Peer] block and Address = 10.8.0.2/32, fd8c:5e2a:91b4::2/128 in its own file. Then enable IPv6 forwarding and start the tunnel again:

echo 'net.ipv6.conf.all.forwarding = 1' >> /etc/sysctl.d/99-wireguard.conf
sysctl --system
systemctl start wg-quick@wg0

With nftables, add ip6 saddr fd8c:5e2a:91b4::/64 oifname "eth0" masquerade to the wg-nat table instead. Public addresses from your /64 are possible too, but depending on how the block reaches your server, they usually need neighbor discovery proxying.

Check for leaks

From a connected device, not from the server, check three things:

  1. Public IP. A what-is-my-IP website must show your server's IPv4 address, plus its IPv6 address if you added IPv6, and never your home address.
  2. DNS. A DNS leak test must list only resolvers of the service in your DNS line, not your internet provider's.
  3. IPv6. Without IPv6 in the tunnel, no IPv6 address should appear at all. If your home IPv6 address shows, check that the device's AllowedIPs contains ::/0.

On the server, wg show then lists a latest handshake for the device and a transfer line that grows as you browse. A tunnel changes the address websites see, not the accounts you sign in to.

Keep your WireGuard VPS updated and add more clients

WireGuard updates arrive with the kernel and wireguard-tools. The automatic security updates from the hardening guide install them; a new kernel applies after a reboot, and wg-quick@wg0 starts by itself. Back up /etc/wireguard, which holds the server's private key.

  • Another device: repeat the phone steps with the next free address, 10.8.0.4/32 and so on.
  • Apply peer changes: systemctl reload wg-quick@wg0 runs wg syncconf, which, says the wg manual, "has the benefit of not disrupting current peer sessions".
  • Remove a device: delete its [Peer] block and reload. Its key stops working at once.
  • Optional: a PresharedKey from wg genpsk, set on both sides of a peer, adds symmetric-key cryptography "for post-quantum resistance".

Troubleshooting

No handshake

No latest handshake for a device in wg show means the key exchange never completed. Check that:

  • the service runs: systemctl status wg-quick@wg0;
  • UFW allows 51820/udp. Some networks block UDP, and WireGuard has no TCP mode, so also test from mobile data;
  • the device's Endpoint has the right address and port;
  • the keys are crossed: the device's file holds the server's public key, and the server's [Peer] block holds the device's.

Handshake but no traffic

A handshake without working websites means the server does not forward or translate the traffic. Check that:

  • sysctl net.ipv4.ip_forward prints 1;
  • the NAT rule names the interface from ip route show default. A wrong name gives exactly this symptom, and the counters in iptables -t nat -L POSTROUTING -n -v show whether the rule matches. Fix it with the tunnel stopped, as described above;
  • UFW has the route allow rule;
  • the device's Address matches its AllowedIPs on the server;
  • names resolve. If ping 9.9.9.9 works but websites do not, fix the DNS line.

Some sites load, others hang: the MTU

wg-quick sets the tunnel's MTU 80 bytes below the local network's: 1420 on a normal 1500-byte link. If a smaller MTU sits further along the path, such as a DSL line using PPPoE behind your router, large packets get lost: small pages load, while downloads and some sites stall. Add MTU = 1380 to the device's [Interface] section and reconnect. If that is not enough, try 1280, the smallest MTU that IPv6 allows.

Frequently asked questions

Running a VPN for yourself is an ordinary use of a server, and our acceptable use policy lists VPNs among the welcome uses on every plan. What you may do through it depends on the law where you are and where the server runs, so check your own jurisdiction. Traffic from the tunnel leaves with your server's IP address.

How many devices can one WireGuard server handle?

The 10.8.0.0/24 range in this guide has room for 253 devices besides the server. In practice the limit is the CPU time that encryption takes and your bandwidth, not the protocol. Watch top during busy hours and move up a plan if the CPU stays near 100%.

Does WireGuard keep logs?

WireGuard writes no connection logs. While the tunnel runs, it keeps each device's last IP address and port in memory, which wg show displays, and journald notes when wg-quick starts and stops. We do not log or inspect customer server traffic, so we keep no record of what passes through your tunnel.

Which port does WireGuard use?

Whichever UDP port you set with ListenPort; this guide uses 51820, the port in WireGuard's own examples. To change it, edit ListenPort, the port in each device's Endpoint and the firewall rule. WireGuard runs over UDP only, so a network that blocks UDP blocks it on any port.

Stuck on a step?

Dedicated and GPU customers can open a ticket from the client area with the server’s IP address and what they tried. First reply target: under 12 hours. For every other server, use the Server actions on its page, the guides and the network status page.

Welcome back

Sign in to manage your servers and your balance.

No KYCHuman check by Cloudflare TurnstileNo tracking