Install on the server

On recent Debian (12+) and Ubuntu (22.04+), WireGuard is a single package:

sudo apt update
sudo apt install wireguard wireguard-tools

No repository setup needed, no dkms, no kernel module compilation — it's in the mainline kernel.

Enable IP forwarding on the server

sudo tee /etc/sysctl.d/99-wireguard.conf <<'EOF'
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF

sudo sysctl --system

Generate keys

One keypair per WireGuard peer, including the server itself. Each peer only needs to know its own private key and the other peer's public key.

umask 077
wg genkey | tee server-private.key | wg pubkey > server-public.key
wg genkey | tee client-private.key | wg pubkey > client-public.key

cat server-private.key server-public.key client-private.key client-public.key
Treat private keys like SSH keys

Anyone with a server-private.key can impersonate your VPN server. Anyone with a client-private.key can connect as that user. chmod 600, keep them in one place, don't paste them into tickets or Slack. I generate them on the machine that'll use them and never move the private key off of it; only the public key gets copied around.

Server config

sudo nano /etc/wireguard/wg0.conf

Paste (substituting your own keys, NIC name, and listen port):

[Interface]
# Server's private key (from server-private.key)
PrivateKey = uDXR7FnTzGarLNj+E3ePv4gOwsbjumZ7M9YjcKAQ8WI=
# VPN subnet — the server's address on the tunnel
Address = 10.0.0.1/24
ListenPort = 51820

# NAT VPN traffic out through the public interface
PostUp   = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# Client's public key (from client-public.key)
PublicKey = JoYcG0Bq5+dMrEAc8eSTG6QCFBjwUWxfXTy7LWmhC0k=
# Client's IP on the VPN subnet (one /32 per peer)
AllowedIPs = 10.0.0.2/32

Verify eth0 is your actual public NIC name (check with ip -br link). On clouds it's often ens3, enp0s3, or similar.

Lock the config down:

sudo chmod 600 /etc/wireguard/wg0.conf

Open the UDP port in your firewall

# UFW
sudo ufw allow 51820/udp

# Pure iptables
sudo iptables -I INPUT -p udp --dport 51820 -j ACCEPT

# On a cloud provider (AWS/GCP/Azure) also open UDP/51820 in the security group

Start the tunnel

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

wg with no arguments shows the current peer state: latest handshake timestamp, bytes sent/received, configured endpoints. This is your first-stop debugging tool.

Windows client

Install the official WireGuard Windows client. It's signed, lightweight, and ships the in-kernel-equivalent Wintun driver.

Open WireGuard, click Add Tunnel → Add empty tunnel…, then paste:

[Interface]
# Client's private key
PrivateKey = 0IoyeQyyWPYVGf4P4DosBGHHrl/T7k+2fqFc8JZRmGo=
# Client's address on the VPN subnet (matches the peer's AllowedIPs on the server)
Address = 10.0.0.2/32
DNS = 1.1.1.1, 1.0.0.1      # optional; prevents DNS leaks to your ISP

[Peer]
# Server's public key
PublicKey = 9XIklpw4lGQ/I0S9L3gqTjwjJYsXJPluihomcCCrEzU=
# Server's public address and port
Endpoint = 5.1.1.1:51820
# 0.0.0.0/0 means "route all IPv4 traffic through the tunnel" (full tunnel)
# Use specific subnets for a split tunnel instead
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

Name it, save, then click Activate.

Verify

Hit dnsleaktest.com or ifconfig.co — you should see your VPS's IP, not your home ISP's. If you see your ISP, the client isn't actually routing through the tunnel; check that AllowedIPs includes 0.0.0.0/0.

From the server side, sudo wg should show a recent handshake timestamp and increasing RX/TX counters.

Common troubleshooting

  • Tunnel comes up, no traffic flows. You forgot net.ipv4.ip_forward = 1, or the iptables MASQUERADE rule didn't install. Run sudo iptables -t nat -L POSTROUTING -v and confirm the rule is there.
  • Handshake never completes. Almost always a firewall: either UFW blocking 51820/udp, or the cloud security group. WireGuard is UDP-only.
  • Works on WiFi, not on mobile data. Some carriers block non-standard UDP ports. Move WireGuard to UDP/443 (same port HTTPS uses) as a workaround — change ListenPort and the client's Endpoint port to 443.
  • Handshake OK but only the VPN subnet is reachable. Your client's AllowedIPs is probably 10.0.0.0/24 instead of 0.0.0.0/0. WireGuard uses AllowedIPs as both a crypto-routing table and a traffic filter; if a subnet isn't listed, it won't be routed through the tunnel.