--- title: "How to Set Up a WireGuard VPN Server on Ubuntu 24.04" description: "Set up a WireGuard VPN server on Ubuntu 24.04: generate keys, enable forwarding and NAT with UFW, add clients with QR codes and route all traffic via the VPN." category: "Use Cases" language: en source: https://cubepath.com/docs/use-cases/wireguard-configuration-as-vpn updated: 2026-09-25 --- # How to Set Up a WireGuard VPN Server on Ubuntu 24.04 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: | Item | Value | | --- | --- | | VPN subnet | `10.8.0.0/24` | | Server VPN address | `10.8.0.1` | | First client address | `10.8.0.2` | | Listen port | `51820/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: ```bash sudo apt update sudo apt install wireguard qrencode ``` Verify the `wg` tool is available: ```bash wg --version ``` ```output 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: ```bash 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: ```bash sudo cat /etc/wireguard/server.key /etc/wireguard/server.pub ``` ```output 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: ```bash echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-wireguard.conf sudo sysctl --system ``` Confirm the value: ```bash sysctl net.ipv4.ip_forward ``` ```output 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: ```bash ip route show default ``` ```output 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: ```bash 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: ```ini [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): ```bash sudo ufw allow 51820/udp sudo ufw route allow in on wg0 out on eth0 ``` Check the rules: ```bash sudo ufw status ``` ```output 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: ```bash sudo systemctl enable --now wg-quick@wg0 ``` Check the interface: ```bash sudo wg show ``` ```output 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. ```bash 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: ```bash sudo nano /etc/wireguard/wg0.conf ``` Append this section, replacing `client1_public_key` with the content of `/etc/wireguard/clients/client1.pub`: ```ini [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: ```bash 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: ```bash 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: ```ini [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: ```bash 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: ```bash sudo apt install wireguard sudo wg-quick up wg0 ``` ## Step 9 - Testing the VPN On the server, check that the client completed a handshake: ```bash sudo wg show ``` ```output 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: ```bash 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: ```ini 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.