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:
| Feature | WireGuard | OpenVPN | IPsec/IKEv2 |
|---|---|---|---|
| Codebase size | ~4,000 lines | ~100,000+ lines | ~400,000+ lines |
| Encryption | Modern (ChaCha20, Curve25519) | Configurable (can be misconfigured) | Configurable |
| Performance | Near wire-speed | Good, but higher overhead | Good |
| Configuration | Simple, minimal | Complex XML/conf files | Very complex |
| Kernel integration | Built into Linux kernel | Userspace | Kernel (varies) |
| Roaming support | Excellent (seamless) | Reconnection required | Protocol-dependent |
| Connection speed | Near-instant | Seconds to establish | Seconds 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 afterdev, typicallyeth0,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, ::/0routes ALL traffic through the VPN (full tunnel). Use10.0.0.0/24for 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
- Download the official WireGuard client from wireguard.com/install
- Install and open the application
- Click Add Tunnel then Add empty tunnel (or import a
.conffile) - Paste the same configuration as the Linux client above
- Click Save and then Activate
macOS Client
- Install WireGuard from the Mac App Store or via Homebrew:
brew install wireguard-tools
- For the GUI app, download it from the Mac App Store
- Click Import tunnel(s) from file and select your
.conffile, or manually create the configuration - 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)
- Install the WireGuard app from the App Store (iOS) or Google Play Store (Android)
- 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:
- Check firewall rules: Ensure UDP port 51820 is open on the server
- Verify keys: The server’s peer section must have the client’s public key, and vice versa
- Check the endpoint: The client must be able to reach the server’s public IP on port 51820
- 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:
- Verify IP forwarding is enabled:
sysctl net.ipv4.ip_forward - Check NAT rules:
sudo iptables -t nat -L POSTROUTING - 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:
- Ensure the
DNSline is present in the client configuration - 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.