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 sudo privileges 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-tools on 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.