WireGuard is a modern VPN built into the Linux kernel. It uses a small set of well-reviewed cryptographic primitives, is configured with short text files, and is typically faster than OpenVPN or IPsec. In this tutorial you will turn an Ubuntu 24.04 server into a WireGuard VPN gateway, connect a laptop and a phone to it, and send all their internet traffic through the server.

Prerequisites

To follow this guide you need:

  • A server running Ubuntu 24.04 LTS with a public IPv4 address, for example a CubePath VPS. WireGuard needs very few resources; 1 vCPU and 512 MB of RAM are enough for a handful of clients.
  • A non-root user with sudo privileges and UFW enabled with SSH allowed.
  • A client device: Windows, macOS, Linux, Android or iOS, with the official WireGuard app or wireguard-tools installed.

The guide uses these values; change them if they collide with a network you already use:

ItemValue
VPN subnet10.8.0.0/24
Server VPN address10.8.0.1
First client address10.8.0.2
Listen port51820/udp

Step 1 - Installing WireGuard

The WireGuard kernel module ships with the Ubuntu kernel, so you only need the userspace tools. Install them together with qrencode, which you will use later to configure phones:

sudo apt update
sudo apt install wireguard qrencode

Verify 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 that 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 -i
cd /etc/wireguard
umask 077
wg genkey | tee server.key | wg pubkey > server.pub
exit

Display both keys; you will need them in the next steps:

sudo cat /etc/wireguard/server.key /etc/wireguard/server.pub
yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=

The first line is the private key, the second the public key.

Step 3 - Enabling IP forwarding

For the server to route traffic from VPN clients to the internet, the kernel must forward packets between interfaces. Enable it permanently with a sysctl drop-in file:

echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system

Confirm the value:

sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

Step 4 - Creating the server configuration

Clients' traffic must leave the server with the server's public IP, which requires a NAT (masquerade) rule on the public interface. Find that interface name:

ip route show default
default via 203.0.113.1 dev eth0 proto static

In this example it is eth0; on some servers it is ens3 or similar. Now create the WireGuard configuration:

sudo nano /etc/wireguard/wg0.conf

Paste the following, replacing server_private_key with the content of server.key 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

The PostUp and PostDown commands add the NAT rule when the interface starts and remove it when it stops. Client [Peer] sections will be added in Step 7.

Step 5 - Allowing WireGuard through UFW

UFW needs two changes: accept incoming WireGuard packets on UDP 51820, and allow forwarding from the VPN interface to the internet (UFW drops forwarded traffic by default):

sudo ufw allow 51820/udp
sudo ufw route allow in on wg0 out on eth0

Check the rules:

sudo ufw status
To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
51820/udp                  ALLOW       Anywhere
Anywhere on eth0           ALLOW FWD   Anywhere on wg0
...

Step 6 - Starting the WireGuard service

wg-quick brings up the interface from wg0.conf, and the wg-quick@ systemd template runs it as a service. Enable and start it:

sudo systemctl enable --now wg-quick@wg0

Check the interface:

sudo wg show
interface: wg0
  public key: xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
  private key: (hidden)
  listening port: 51820

If the service fails, journalctl -u wg-quick@wg0 shows which line of the configuration it could not apply.

Step 7 - Adding a client

Generate a key pair for the first client. Doing this on the server is convenient for phones, which you will configure by scanning a QR code; for laptops you can also generate the keys on the client and only copy its public key to the server.

sudo -i
mkdir -p /etc/wireguard/clients
cd /etc/wireguard/clients
umask 077
wg genkey | tee client1.key | wg pubkey > client1.pub
exit

Add the client as a peer on the server. Open the server configuration again:

sudo nano /etc/wireguard/wg0.conf

Append this section, replacing client1_public_key with the content of /etc/wireguard/clients/client1.pub:

[Peer]
# client1
PublicKey = client1_public_key
AllowedIPs = 10.8.0.2/32

On the server side, AllowedIPs lists the addresses that this peer is allowed to use as source; each client gets its own /32. Apply the change without dropping connected clients:

sudo systemctl reload wg-quick@wg0

The reload runs wg syncconf, which only updates what changed. sudo wg show now lists the peer.

Next, write the client's configuration file:

sudo nano /etc/wireguard/clients/client1.conf

Replace client1_private_key with the content of client1.key, server_public_key with server.pub and your_server_ip with the server's public IP:

[Interface]
PrivateKey = client1_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

On the client side, AllowedIPs = 0.0.0.0/0 means "send all IPv4 traffic through the tunnel". PersistentKeepalive keeps NAT mappings on home routers and mobile networks open so the server can always reach the client.

Step 8 - Connecting the client

Phones (Android and iOS)

Install the WireGuard app, tap + and choose Scan from QR code. Show the configuration as a QR code in your terminal:

sudo qrencode -t ansiutf8 < /etc/wireguard/clients/client1.conf

Scan it, give the tunnel a name and turn it on.

Windows and macOS

Install the WireGuard app from wireguard.com or the Mac App Store, choose Import tunnel(s) from file, and select a copy of client1.conf. Transfer the file over SSH, for example with scp, and delete it from the client's download folder once imported, since it contains a private key.

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

Step 9 - Testing the VPN

On the server, check that the client completed a handshake:

sudo wg show
peer: 3Q8Z0...client1 public key...=
  endpoint: 198.51.100.23:53012
  allowed ips: 10.8.0.2/32
  latest handshake: 12 seconds ago
  transfer: 1.24 MiB received, 8.73 MiB sent

From the client, ping the server's VPN address and check which public IP the internet sees:

ping -c 3 10.8.0.1
curl -4 https://ifconfig.me

The curl command should print your server's public IP, not the client's. On a phone, open any "what is my IP" website instead.

Adding more clients and split tunneling

For each additional device, repeat Step 7 with a new key pair and the next free address (10.8.0.3/32, 10.8.0.4/32 and so on). Never reuse a key pair on two devices: WireGuard identifies peers by public key, and two devices with the same key keep taking the tunnel from each other.

If you only want to reach the VPN network (for example private services on the server) and keep normal browsing on the client's own connection, change AllowedIPs in the client file to the VPN subnet and remove the DNS line:

AllowedIPs = 10.8.0.0/24

Troubleshooting

  • No latest handshake line on the server. The packets are not arriving. Check sudo ufw status for 51820/udp, confirm the Endpoint IP and port in the client file, and make sure the client uses the server's public key (not its own).
  • Handshake works but there is no internet. Forwarding or NAT is missing. Verify sysctl net.ipv4.ip_forward returns 1, that the interface in PostUp matches ip route show default, and that the ufw route allow rule exists.
  • Web pages don't load but pings to IPs work. DNS is the problem. Check the DNS line in the client file. On Linux clients, wg-quick needs a resolvconf implementation; if it reports resolvconf: command not found, install openresolv or import the file with nmcli connection import type wireguard file wg0.conf instead.
  • Some sites hang while others work. This is usually an MTU problem on links with extra encapsulation (PPPoE, mobile networks). Add MTU = 1380 to the client's [Interface] section and reconnect.

Conclusion

You now have a WireGuard VPN server on Ubuntu 24.04 that routes clients' traffic through its public IP, with a repeatable procedure to add devices. As next steps, consider adding IPv6 to the tunnel, using the VPN to reach private services such as a Samba share or a database without exposing them to the internet, and keeping the clients directory in an encrypted backup so you can revoke or restore peers.