Set Up a WireGuard VPN Server on Ubuntu 24.04
WireGuard is a modern VPN: small, fast and configured with a few lines. This guide builds a server on Ubuntu 24.04 and connects one client, either routing all traffic through the server or just reaching its private network.
How WireGuard thinks
- Every device has a key pair. You exchange only the public keys.
- Each peer has an
AllowedIPslist that acts as both a routing table (which traffic goes into the tunnel) and an access filter (which source addresses are accepted from that peer). - There are no "servers" at the protocol level, only peers. The one with a public IP and a fixed port plays the server.
- It uses UDP only (default port
51820).
Plan: the VPN network will be 10.8.0.0/24. Server = 10.8.0.1, first client = 10.8.0.2. Choose a range that doesn't overlap your home or office LAN. See subnetting basics.
1. Install and create the server keys
sudo apt update && sudo apt install -y wireguard
sudo sh -c 'umask 077; wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub'
sudo cat /etc/wireguard/server.pub
2. Create a key pair for the client
Run this on the client (so its private key never leaves it). On Linux:
sudo apt install -y wireguard
umask 077; wg genkey | tee client.key | wg pubkey > client.pub
cat client.pub
The Windows, macOS, Android and iOS apps generate keys for you when you add an empty tunnel.
3. Server configuration
Find your public network interface name (eth0 below):
ip -br addr
Create /etc/wireguard/wg0.conf:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>
# NAT, so clients can reach the internet through the server (full-tunnel mode)
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]
# my laptop
PublicKey = <contents of client.pub>
AllowedIPs = 10.8.0.2/32
sudo chmod 600 /etc/wireguard/wg0.conf
4. Enable forwarding and open the firewall
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sudo ufw allow 51820/udp
sudo ufw route allow in on wg0 out on eth0
If you use a cloud firewall at your hosting provider, allow UDP 51820 there too. See securing a new server for the basic firewall setup.
5. Start it
sudo systemctl enable --now wg-quick@wg0
sudo wg show
wg show lists the interface and peers. A peer with no latest handshake hasn't connected yet.
6. Client configuration
[Interface]
PrivateKey = <contents of client.key>
Address = 10.8.0.2/24
DNS = 1.1.1.1
[Peer]
PublicKey = <contents of /etc/wireguard/server.pub>
Endpoint = SERVER_PUBLIC_IP:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Choose AllowedIPs according to what you want:
AllowedIPs |
Effect |
|---|---|
0.0.0.0/0 |
Full tunnel: all IPv4 traffic goes through the server |
10.8.0.0/24 |
Split tunnel: only VPN-network traffic. Normal internet stays direct |
10.8.0.0/24, 192.168.50.0/24 |
VPN network plus a LAN behind the server |
Don't add ::/0 for full tunnel unless the server has working IPv6 forwarding. Otherwise all IPv6 traffic on the client disappears into the tunnel and goes nowhere.
On a Linux client save it as /etc/wireguard/wg0.conf and run:
sudo wg-quick up wg0
curl ifconfig.me # shows the server's IP in full-tunnel mode
sudo wg-quick down wg0
For a phone, show the config as a QR code on the machine that has it:
sudo apt install -y qrencode
qrencode -t ansiutf8 < client.conf
Scan it from the WireGuard app.
Adding more clients
Give each client its own key pair and its own 10.8.0.x/32, and add another [Peer] block on the server. Apply without dropping existing connections:
sudo wg syncconf wg0 <(sudo wg-quick strip wg0)
Troubleshooting
| Symptom | Check |
|---|---|
No handshake in wg show |
UDP 51820 not reachable: server firewall, cloud firewall, wrong Endpoint, wrong public key on either side |
| Handshake OK, no internet through the VPN | ip_forward off, missing MASQUERADE rule, wrong interface name in PostUp, or ufw route missing |
Can ping 10.8.0.1 but nothing else |
Forwarding/NAT again, or AllowedIPs too narrow |
| Some sites hang | MTU too high, so set MTU = 1380 in the client [Interface] |
| DNS fails once connected | Set DNS = on the client, and make sure the resolver is reachable through the tunnel |
| Works on Wi-Fi, not on mobile data | Some carriers block UDP ports, so try another port such as 443 |
Debug on the server with:
sudo wg show
sudo tcpdump -ni any udp port 51820
If packets arrive but there's no handshake, the keys are mismatched. If nothing arrives, it's a firewall or DNS problem on the way (see diagnosing connectivity).
Cloudflare note: the orange-cloud proxy only handles HTTP(S). A VPN endpoint must use a DNS-only record (grey cloud) or the plain IP address.
Security notes
- Keep the private keys (
*.key, and thePrivateKeyline) secret, and never commit them to Git. AllowedIPson the server restricts what each client can spoof, so use/32per client.- Removing access is simple: delete the peer on the server.
- You can also add a pre-shared key per peer (
wg genpsk), which adds an extra layer of symmetric-key protection on top of the normal key exchange.
Easier alternative
Tailscale is built on WireGuard and handles keys, NAT traversal and DNS for you, with no open ports on the server. It's a good choice if you mostly want to reach your own machines securely. Hand-written WireGuard gives you full control and no third-party coordination server.