WIREGUARD VPN TUNNEL Client 10.0.0.2/32 Private Key wg0 interface Encrypted Tunnel UDP port 51820 VPN Server 10.0.0.1/24 IP Forwarding NAT / iptables Internet Public Web All traffic between client and server is encrypted via WireGuard tunnel

WireGuard is a modern, high-performance VPN protocol that has rapidly become the preferred choice for system administrators and security professionals. Built directly into the Linux kernel since version 5.6, WireGuard offers a dramatically simpler configuration, smaller attack surface, and superior performance compared to traditional VPN solutions like OpenVPN and IPsec/IKEv2.

This guide walks you through setting up a WireGuard VPN server on Ubuntu, configuring clients for Linux, Windows, macOS, and mobile devices, and securing the entire setup with proper firewall rules.

Prerequisites

Before you begin, make sure you have:

  • An Ubuntu server (22.04 or 24.04) with a public IP address
  • Root or sudo access to the server
  • Terminal access via SSH
  • A client device (Linux, Windows, macOS, iOS, or Android)
  • Basic understanding of networking concepts (IP addresses, subnets, ports)

Why WireGuard Over OpenVPN or IPsec?

Before diving into the setup, here is why WireGuard has become the go-to VPN protocol:

FeatureWireGuardOpenVPNIPsec/IKEv2
Codebase size~4,000 lines~100,000+ lines~400,000+ lines
EncryptionModern (ChaCha20, Curve25519)Configurable (can be misconfigured)Configurable
PerformanceNear wire-speedGood, but higher overheadGood
ConfigurationSimple, minimalComplex XML/conf filesVery complex
Kernel integrationBuilt into Linux kernelUserspaceKernel (varies)
Roaming supportExcellent (seamless)Reconnection requiredProtocol-dependent
Connection speedNear-instantSeconds to establishSeconds to establish

WireGuard’s small codebase makes it significantly easier to audit for security vulnerabilities. Its use of modern cryptographic primitives means there are no cipher negotiation vulnerabilities, and its kernel-space implementation delivers throughput that OpenVPN’s userspace approach cannot match.

Step 1: Install WireGuard

WireGuard is available in the default Ubuntu repositories. Install it along with the required tools:

sudo apt update
sudo apt install -y wireguard wireguard-tools

Verify the installation:

wg --version

Ensure the WireGuard kernel module is loaded:

sudo modprobe wireguard
lsmod | grep wireguard

Step 2: Generate Server Keys

WireGuard uses public-key cryptography. Each peer (server and client) needs its own private and public key pair.

Create a directory for the keys with restrictive permissions and generate the server key pair:

wg genkey | sudo tee /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key

Lock down the private key file:

sudo chmod 600 /etc/wireguard/server_private.key

View the keys (you will need these for configuration):

sudo cat /etc/wireguard/server_private.key
sudo cat /etc/wireguard/server_public.key

Security Warning: The private key must never be shared or transmitted over insecure channels. Treat it like a password. If compromised, generate a new key pair immediately.

Step 3: Configure the WireGuard Server

Create the WireGuard interface configuration file:

sudo nano /etc/wireguard/wg0.conf

Add the following configuration, replacing the placeholder values:

[Interface]
# The server's private key (from server_private.key)
PrivateKey = <SERVER_PRIVATE_KEY>

# VPN subnet - the server gets .1
Address = 10.0.0.1/24

# UDP port WireGuard will listen on
ListenPort = 51820

# NAT rules - applied when the interface comes up/down
# Replace eth0 with your actual network interface name
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -A FORWARD -o wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -o wg0 -j ACCEPT

# Peer configurations will be added below

Tip: To find your server’s main network interface name, run ip route show default. The interface is listed after dev, typically eth0, ens3, enp0s3, or similar.

Set restrictive permissions on the configuration file:

sudo chmod 600 /etc/wireguard/wg0.conf

Step 4: Enable IP Forwarding

For the VPN server to route traffic between clients and the internet, IP forwarding must be enabled:

echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
echo "net.ipv6.conf.all.forwarding = 1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

Verify the setting:

sysctl net.ipv4.ip_forward

The output should show net.ipv4.ip_forward = 1.

Step 5: Configure the Firewall (UFW)

If you are using UFW (Uncomplicated Firewall), allow the WireGuard port and SSH:

# Allow WireGuard traffic
sudo ufw allow 51820/udp

# Ensure SSH is allowed (so you do not lock yourself out)
sudo ufw allow OpenSSH

# Enable UFW if not already enabled
sudo ufw enable

# Verify the rules
sudo ufw status verbose

You also need to allow forwarded traffic in UFW. Edit the UFW configuration:

sudo nano /etc/ufw/before.rules

Add the following lines before the *filter section at the top of the file:

# NAT table rules for WireGuard
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -o eth0 -j MASQUERADE
COMMIT

Then edit the UFW default forward policy:

sudo nano /etc/default/ufw

Change the DEFAULT_FORWARD_POLICY from DROP to ACCEPT:

DEFAULT_FORWARD_POLICY="ACCEPT"

Reload UFW:

sudo ufw reload

For more information on securing your server’s SSH access, see our article on storing SSH public keys and resolving SSH permission issues.

Step 6: Generate Client Keys and Add a Peer

For each client that will connect to the VPN, generate a key pair:

wg genkey | tee client1_private.key | wg pubkey > client1_public.key

You can also generate a preshared key for an additional layer of symmetric encryption:

wg genpsk > client1_preshared.key

Now add the client as a peer in the server configuration. Edit /etc/wireguard/wg0.conf and add:

[Peer]
# Client 1
PublicKey = <CLIENT1_PUBLIC_KEY>
PresharedKey = <CLIENT1_PRESHARED_KEY>
AllowedIPs = 10.0.0.2/32

The AllowedIPs field specifies which IP addresses this peer is allowed to use. For a single client, use /32 (a single host).

Step 7: Start the WireGuard Service

Start the WireGuard interface and enable it to start on boot:

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0

Verify the interface is running:

sudo wg show

You should see the interface details, the listening port, and the configured peers. To check the interface IP address:

ip addr show wg0

Client Configuration

Linux Client

Install WireGuard on the client machine:

sudo apt install -y wireguard

Create the client configuration file:

sudo nano /etc/wireguard/wg0.conf
[Interface]
PrivateKey = <CLIENT1_PRIVATE_KEY>
Address = 10.0.0.2/24
DNS = 1.1.1.1, 8.8.8.8

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
PresharedKey = <CLIENT1_PRESHARED_KEY>
Endpoint = <SERVER_PUBLIC_IP>:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

Configuration explained:

  • Address: The client’s IP within the VPN subnet
  • DNS: DNS servers to use when the VPN is active
  • Endpoint: The server’s public IP and WireGuard port
  • AllowedIPs: 0.0.0.0/0, ::/0 routes ALL traffic through the VPN (full tunnel). Use 10.0.0.0/24 for split tunneling (only VPN subnet traffic goes through the tunnel)
  • PersistentKeepalive: Sends a keepalive packet every 25 seconds, which is essential for clients behind NAT

Start the VPN connection:

sudo wg-quick up wg0

To disconnect:

sudo wg-quick down wg0

To enable automatic connection on boot:

sudo systemctl enable wg-quick@wg0

Windows Client

  1. Download the official WireGuard client from wireguard.com/install
  2. Install and open the application
  3. Click Add Tunnel then Add empty tunnel (or import a .conf file)
  4. Paste the same configuration as the Linux client above
  5. Click Save and then Activate

macOS Client

  1. Install WireGuard from the Mac App Store or via Homebrew:
brew install wireguard-tools
  1. For the GUI app, download it from the Mac App Store
  2. Click Import tunnel(s) from file and select your .conf file, or manually create the configuration
  3. Activate the tunnel from the app or menu bar icon

For command-line usage on macOS:

sudo wg-quick up /path/to/wg0.conf

iOS and Android (Mobile Devices)

  1. Install the WireGuard app from the App Store (iOS) or Google Play Store (Android)
  2. You can either manually enter the configuration or scan a QR code

Generating a QR Code for Mobile

This is the most convenient method for mobile setup. On the server, install qrencode:

sudo apt install -y qrencode

Create a temporary configuration file for the client:

cat << 'EOF' > /tmp/client1-mobile.conf
[Interface]
PrivateKey = <CLIENT1_PRIVATE_KEY>
Address = 10.0.0.2/24
DNS = 1.1.1.1, 8.8.8.8

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
PresharedKey = <CLIENT1_PRESHARED_KEY>
Endpoint = <SERVER_PUBLIC_IP>:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
EOF

Generate and display the QR code in the terminal:

qrencode -t ansiutf8 < /tmp/client1-mobile.conf

Open the WireGuard app on your phone, tap the + button, select Create from QR code, and scan the code displayed in your terminal.

Security Tip: Delete the temporary configuration file immediately after scanning:

rm -f /tmp/client1-mobile.conf

Testing the VPN Connection

After connecting a client, verify the VPN is working correctly.

On the client, check your VPN IP and public IP:

# Check the WireGuard interface
sudo wg show

# Verify you have the VPN IP
ip addr show wg0

# Check your public IP (should show the server's IP if using full tunnel)
curl -s https://ifconfig.me

# Test connectivity to the server's VPN IP
ping 10.0.0.1

On the server, verify the peer is connected:

sudo wg show

You should see a latest handshake timestamp and transfer data for the connected peer. If these values are updating, the tunnel is active and passing traffic.

Adding More Clients

To add additional clients, repeat the key generation process with unique keys and IP addresses:

# Generate keys for client 2
wg genkey | tee client2_private.key | wg pubkey > client2_public.key
wg genpsk > client2_preshared.key

Add the new peer to the server configuration. You can do this without restarting the service:

sudo wg set wg0 peer <CLIENT2_PUBLIC_KEY> preshared-key client2_preshared.key allowed-ips 10.0.0.3/32

To make the change persistent, also add the [Peer] block to /etc/wireguard/wg0.conf.

Troubleshooting

Handshake Does Not Complete

If sudo wg show never shows a latest handshake for a peer:

  1. Check firewall rules: Ensure UDP port 51820 is open on the server
  2. Verify keys: The server’s peer section must have the client’s public key, and vice versa
  3. Check the endpoint: The client must be able to reach the server’s public IP on port 51820
  4. Test UDP connectivity: nc -u -z <SERVER_IP> 51820

Traffic Not Routing Through VPN

If the handshake completes but internet traffic does not flow through the VPN:

  1. Verify IP forwarding is enabled: sysctl net.ipv4.ip_forward
  2. Check NAT rules: sudo iptables -t nat -L POSTROUTING
  3. Verify the correct network interface in the PostUp/PostDown rules matches your server’s actual interface

DNS Leaks

If DNS queries are not going through the VPN:

  1. Ensure the DNS line is present in the client configuration
  2. On Linux clients, you may need to configure systemd-resolved:
sudo resolvectl dns wg0 1.1.1.1 8.8.8.8

Connection Drops Behind NAT

If the VPN connection drops after a period of inactivity, ensure PersistentKeepalive = 25 is set in the client’s [Peer] section. This sends a keepalive packet every 25 seconds to maintain the NAT mapping.

Revoking a Client

To remove a client’s access, delete their peer from the server:

sudo wg set wg0 peer <CLIENT_PUBLIC_KEY> remove

Also remove the corresponding [Peer] block from /etc/wireguard/wg0.conf to make the change persistent.

Conclusion

WireGuard provides a fast, modern, and secure VPN solution that is remarkably easy to set up compared to its predecessors. With its kernel-level integration, minimal configuration, and state-of-the-art cryptography, it is an excellent choice for both personal use and enterprise deployments.

For a more secure overall server setup, consider also reviewing your SSH configuration. See our guides on adding SSH keys to another machine and managing SSH connections. For advanced networking configurations on Ubuntu, check out our article on assigning multiple IP addresses to a single interface.