Initech
KB-16 Print

How to Set Up WireGuard on a VPS

Quick answer: install WireGuard, generate a key pair for the server and each client, write a server config that enables IP forwarding and a NAT masquerade rule, then bring the tunnel up:

apt update && apt install -y wireguard
wg genkey | tee server_private.key | wg pubkey > server_public.key
systemctl enable --now wg-quick@wg0

The single most common failure is a tunnel that connects but carries no traffic. That is almost always a missing IP forwarding setting or a missing masquerade rule, both covered below.

Step 1: install WireGuard

On a current Ubuntu or Debian VPS, WireGuard ships in the standard repositories:

apt update
apt install -y wireguard

This installs the wg and wg-quick tools. On modern kernels the WireGuard module is already built in, so there is nothing else to load.

Step 2: generate keys

Create a private and public key for the server. The umask 077 keeps the private key readable only by root:

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

Generate a key pair for each client the same way (run this on the client, or generate on the server and hand the private key over securely):

wg genkey | tee client_private.key | wg pubkey > client_public.key

A WireGuard key pair is two matched files. The private key never leaves the machine it belongs to. Only public keys are exchanged between server and client.

Step 3: write the server config

Create /etc/wireguard/wg0.conf. Replace the key placeholders with the real contents of the files you generated, and replace eth0 with your VPS network interface if it differs (check with ip route show default):

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <server_private.key contents>
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = <client_public.key contents>
AllowedIPs = 10.8.0.2/32

The PostUp and PostDown lines add and remove the NAT masquerade rule automatically when the tunnel starts and stops. Without masquerade, packets from clients reach the server and then vanish, because the wider internet has no route back to the private 10.8.0.0/24 range.

Step 4: enable IP forwarding

By default a Linux server does not forward packets between interfaces. Turn it on permanently:

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

If you also route IPv6 through the tunnel, add net.ipv6.conf.all.forwarding = 1 to the same file.

Step 5: add a client config

On the client, create a config that points at your server public IP and routes all traffic through the tunnel:

[Interface]
Address = 10.8.0.2/24
PrivateKey = <client_private.key contents>
DNS = 1.1.1.1

[Peer]
PublicKey = <server_public.key contents>
Endpoint = your-server-ip:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Setting AllowedIPs = 0.0.0.0/0 on the client sends all of its traffic over the tunnel. Use a narrower range if you only want to reach specific networks. PersistentKeepalive keeps the connection alive through NAT on the client side.

Step 6: bring the tunnel up

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

wg show lists peers and, once a client connects, a recent handshake and transfer counters. systemctl enable makes the tunnel start on boot.

Troubleshooting: connects but no traffic

This is the failure that generates the most support questions. Work through these in order:

  • IP forwarding is off. Run sysctl net.ipv4.ip_forward. If it returns 0, step 4 did not take. Re-apply it and confirm it survives a reboot.
  • The masquerade rule is missing. Run iptables -t nat -L POSTROUTING -n -v and look for a MASQUERADE line on your outbound interface. If it is absent, your PostUp line has the wrong interface name or the tunnel was started before the rule existed. Restart with wg-quick down wg0 && wg-quick up wg0.
  • Wrong outbound interface. The interface in the PostUp rule must match the one with the default route. Confirm with ip route show default; it is often eth0 but can be ens3, enp1s0 or similar on cloud images.
  • A firewall is blocking UDP 51820. WireGuard uses UDP, not TCP. If your provider or cloud platform has a network firewall in front of the VPS, open inbound UDP on your ListenPort there as well as in any local firewall. A tunnel that never completes a handshake (no recent handshake in wg show) usually means the port is blocked.
  • DNS fails but IPs work. If you can ping 1.1.1.1 through the tunnel but names do not resolve, the client DNS line is missing or the resolver is unreachable.

Handshake never happens

If wg show never lists a handshake, the client is not reaching the server at all. Check that the server public IP and port in the client Endpoint are correct, that the server is actually listening (ss -ulnp | grep 51820), and that no network firewall sits in the path dropping the UDP packets.

A note on acceptable use

Running your own VPN server on a VPS is a fully supported use case, and the traffic that flows through it is treated like any other workload under our acceptable use policy.

Was this answer helpful?
Related Articles