WireGuard is a modern VPN built into the Linux kernel. It uses current cryptography, has a small configuration surface and is fast enough to route all of a device's traffic without noticeable overhead. In this tutorial you will set up a WireGuard server on Ubuntu 24.04, configure forwarding and NAT so clients reach the internet through it, protect it with UFW and connect your first client from a laptop or phone.
Prerequisites
To follow this guide you need:
- A server running Ubuntu 24.04 LTS with a public IPv4 address, for example a CubePath VPS.
- A non-root user with
sudoprivileges and UFW available (it is installed by default on Ubuntu). - A client device: Windows, macOS, Linux, Android or iOS, with the official WireGuard app, or
wireguard-toolson Linux.
This guide uses the VPN subnet 10.8.0.0/24, with the server at 10.8.0.1 and the first client at 10.8.0.2, and the default WireGuard port 51820/udp. Replace your_server_ip with your server's public IP.
Step 1 - Installing WireGuard
WireGuard is in Ubuntu's main repository and the kernel module ships with the stock kernel. Install the userspace tools, plus qrencode to share client configurations with phones:
sudo apt update
sudo apt install wireguard qrencode
Check that the wg tool is available:
wg --version
wireguard-tools v1.0.20210914 - https://git.zx2c4.com/wireguard-tools/
Step 2 - Generating the server keys
Each WireGuard peer has a private key, which never leaves the machine, and a public key that you share with the other side. Generate the server's key pair in /etc/wireguard with a restrictive umask so the private key is only readable by root:
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server.key > /etc/wireguard/server.pub'
Check the permissions and display both keys, you will need them in the next steps:
sudo ls -l /etc/wireguard/
sudo cat /etc/wireguard/server.key /etc/wireguard/server.pub
-rw------- 1 root root 45 Sep 25 10:12 server.key
-rw-r--r-- 1 root root 45 Sep 25 10:12 server.pub
Step 3 - Generating the first client's keys
For simplicity, generate the client's key pair on the server too and store it in a separate directory. You will delete the client's private key from the server once the client is configured:
sudo mkdir -m 700 /etc/wireguard/clients
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/clients/laptop.key'
sudo sh -c 'wg pubkey < /etc/wireguard/clients/laptop.key > /etc/wireguard/clients/laptop.pub'
Step 4 - Configuring the WireGuard interface
Find the name of the server's public interface, which the NAT rule needs:
ip route show default
default via 198.51.100.1 dev eth0 proto static
In this example it is eth0. Now create the server configuration:
sudo nano /etc/wireguard/wg0.conf
Paste the following, replacing server_private_key with the content of /etc/wireguard/server.key, laptop_public_key with the content of /etc/wireguard/clients/laptop.pub, and eth0 with your interface name:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = server_private_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 = laptop_public_key
AllowedIPs = 10.8.0.2/32
The PostUp and PostDown lines add and remove a NAT rule, so traffic from VPN clients leaves the server with its public IP. AllowedIPs on the server side says which VPN address belongs to each peer.
Restrict the file to root, since it contains the private key:
sudo chmod 600 /etc/wireguard/wg0.conf
Step 5 - Enabling IP forwarding
The server must forward packets between the VPN interface and the public interface. Create a sysctl file for it:
sudo nano /etc/sysctl.d/99-wireguard.conf
net.ipv4.ip_forward = 1
Apply it and confirm the value:
sudo sysctl --system
sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
Step 6 - Configuring the firewall
Allow SSH first so you do not lock yourself out, then the WireGuard port. UFW drops forwarded traffic by default, so also add a route rule that lets VPN clients go out through the public interface:
sudo ufw allow OpenSSH
sudo ufw allow 51820/udp
sudo ufw route allow in on wg0 out on eth0
sudo ufw enable
Check the result:
sudo ufw status verbose
Status: active
Default: deny (incoming), allow (outgoing), deny (routed)
To Action From
-- ------ ----
22/tcp (OpenSSH) ALLOW IN Anywhere
51820/udp ALLOW IN Anywhere
Anywhere on eth0 ALLOW FWD Anywhere on wg0
Step 7 - Starting the WireGuard service
wg-quick ships with a systemd template unit. Enable it for wg0 so the VPN starts now and on every boot:
sudo systemctl enable --now wg-quick@wg0
Verify that the service is active and the interface is up:
sudo systemctl status wg-quick@wg0 --no-pager
sudo wg show
interface: wg0
public key: Hx3yZ2Wq0...=
private key: (hidden)
listening port: 51820
peer: 9kLmN4Pq...=
allowed ips: 10.8.0.2/32
If the service fails, check the logs with sudo journalctl -u wg-quick@wg0.
Step 8 - Creating the client configuration
Create the configuration file for the client:
sudo nano /etc/wireguard/clients/laptop.conf
Replace laptop_private_key with the content of /etc/wireguard/clients/laptop.key, server_public_key with the content of /etc/wireguard/server.pub, and your_server_ip with the server's public IP:
[Interface]
PrivateKey = laptop_private_key
Address = 10.8.0.2/32
DNS = 1.1.1.1, 9.9.9.9
[Peer]
PublicKey = server_public_key
Endpoint = your_server_ip:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
AllowedIPs = 0.0.0.0/0 sends all of the client's IPv4 traffic through the VPN. If you only want to reach the VPN subnet, use 10.8.0.0/24 instead. PersistentKeepalive keeps the connection alive when the client is behind NAT.
Step 9 - Connecting the client
Phones (Android and iOS): install the WireGuard app, tap +, choose to scan a QR code and scan the code the server prints with:
sudo qrencode -t ansiutf8 < /etc/wireguard/clients/laptop.conf
Windows and macOS: copy laptop.conf to the computer over a secure channel (for example scp), then use Import tunnel(s) from file in the WireGuard app and activate the tunnel.
Linux: install the tools, copy the file to /etc/wireguard/wg0.conf on the client and bring the tunnel up:
sudo apt install wireguard
sudo wg-quick up wg0
Once the client is configured, remove its private key and configuration from the server:
sudo rm /etc/wireguard/clients/laptop.key /etc/wireguard/clients/laptop.conf
Step 10 - Verifying the VPN
On the client, check that your public IP is now the server's IP:
curl -4 https://ifconfig.me
203.0.113.10
Ping the server over the tunnel:
ping -c 3 10.8.0.1
On the server, sudo wg show now shows a recent handshake and traffic counters for the peer:
peer: 9kLmN4Pq...=
endpoint: 192.0.2.44:53122
allowed ips: 10.8.0.2/32
latest handshake: 12 seconds ago
transfer: 1.24 MiB received, 8.91 MiB sent
Adding more clients
For every new device, repeat Steps 3 and 8 with a new name and the next free address (10.8.0.3, 10.8.0.4...). Then add a [Peer] block for it to /etc/wireguard/wg0.conf:
[Peer]
# phone
PublicKey = phone_public_key
AllowedIPs = 10.8.0.3/32
Reload the configuration without disconnecting the existing clients:
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'
To revoke a device, delete its [Peer] block and run the same command.
Troubleshooting
No handshake appears in wg show. The UDP packets are not reaching the server. Check that 51820/udp is allowed in UFW and in any other firewall in front of the server, and that the client's Endpoint and the server's public key are correct.
The client connects but has no internet. Check that net.ipv4.ip_forward is 1, that the ufw route allow rule exists and that the interface name in the PostUp NAT rule matches your public interface. sudo iptables -t nat -L POSTROUTING -n should list the MASQUERADE rule.
wg-quick on a Linux client fails with resolvconf: command not found. The DNS line needs a resolvconf implementation. Install openresolv on the client with sudo apt install openresolv, or remove the DNS line.
Conclusion
You now have your own WireGuard VPN server on Ubuntu 24.04 that routes client traffic through its public IP, starts at boot and is protected by UFW. As next steps, add IPv6 to the tunnel if your server has an IPv6 address, restrict SSH on the server so it is only reachable through the VPN (10.8.0.1), and keep a record of which public key belongs to each device so you can revoke them quickly.
