← Back to DevBytes

How to Set Up a WireGuard VPN on Ubuntu Server

What is WireGuard?

WireGuard is a modern, high-performance VPN protocol designed with simplicity and security at its core. Unlike legacy VPN solutions such as OpenVPN or IPsec, WireGuard operates with a minimal codebase — around 4,000 lines — making it dramatically easier to audit, maintain, and deploy. It runs entirely within the Linux kernel (since kernel 5.6) and uses state-of-the-art cryptography including Curve25519 for key exchange, ChaCha20 for encryption, and Poly1305 for authentication.

WireGuard functions as a point-to-point tunnel using UDP, creating a secure network interface (wg0) on both the server and client machines. Traffic is encrypted between these interfaces, and peers are identified by their public keys rather than traditional username/password authentication. The protocol is connectionless, meaning it handles roaming seamlessly — a client can switch from Wi-Fi to cellular without dropping the tunnel.

Why WireGuard Matters

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

For developers and system administrators, WireGuard offers several compelling advantages over traditional VPN technologies:

Common use cases include securing remote access to internal services, connecting distributed microservices across cloud regions, creating site-to-site links between offices, and providing privacy when using untrusted networks such as public Wi-Fi.

Prerequisites

Step-by-Step Server Setup

Step 1: Update Your System and Install WireGuard

Begin by updating the package index and installing WireGuard. On Ubuntu 22.04 and newer, WireGuard tools are available in the standard repositories. For older LTS releases, you may need to enable the wireguard-tools backport.

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

If your kernel is older than 5.6, you will also need to install the DKMS kernel module:

sudo apt install wireguard-dkms -y

Verify the installation by checking that the wg command is available:

wg --version

Step 2: Generate Private and Public Keys

WireGuard uses asymmetric cryptography to authenticate peers. Each node in the VPN must have its own key pair. Create a directory to store the WireGuard configuration and keys with restricted permissions:

sudo mkdir -p /etc/wireguard
sudo chmod 700 /etc/wireguard

Generate the server's private key and derive the public key from it:

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

Take note of both keys — you will need the private key for the server configuration and the public key for client configurations. To display them at any time:

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

Step 3: Create the Server Configuration File

Create the WireGuard configuration file for the server interface. This file defines the tunnel's IP address, listening port, private key, and any firewall rules for post-up and pre-down hooks.

sudo nano /etc/wireguard/wg0.conf

Populate it with the following content. Adjust the Address, PrivateKey, and other values to match your environment:

[Interface]
# The private key for this server node
PrivateKey = YOUR_SERVER_PRIVATE_KEY_HERE

# The IPv4 and/or IPv6 address assigned to the WireGuard interface on the server
# This address should be in a private range not used elsewhere on your network
Address = 10.0.0.1/24

# The UDP port WireGuard listens on for incoming peer connections
ListenPort = 51820

# Optional: Run commands after the interface comes up
# Enable IP forwarding and set up NAT for VPN clients
PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostUp = iptables -A FORWARD -o wg0 -j ACCEPT

# Optional: Run commands before the interface goes down
# Clean up firewall rules when the tunnel is deactivated
PreDown = iptables -D FORWARD -i wg0 -j ACCEPT
PreDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
PreDown = iptables -D FORWARD -o wg0 -j ACCEPT

# Save the firewall rules so they persist across reboots
# (Alternative to using iptables-persistent)
PostUp = netfilter-persistent save
PreDown = netfilter-persistent save

Replace YOUR_SERVER_PRIVATE_KEY_HERE with the actual private key from /etc/wireguard/server_private.key. Also, verify that eth0 matches your server's primary network interface — use ip a to check and adjust accordingly.

Step 4: Configure IP Forwarding Permanently

While the PostUp rule enables IP forwarding temporarily, it is best to set this permanently in the system configuration:

sudo nano /etc/sysctl.d/99-wireguard.conf

Add the following line:

net.ipv4.ip_forward = 1

Apply the change immediately:

sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

If you also need IPv6 forwarding, add net.ipv6.conf.all.forwarding = 1 to the same file.

Step 5: Configure the Firewall

If UFW (Uncomplicated Firewall) is enabled on your server, you must allow UDP traffic on the WireGuard port and allow forwarding:

sudo ufw allow 51820/udp
sudo ufw allow OpenSSH  # Ensure SSH access is not blocked
sudo ufw enable

If you are using raw iptables without UFW, ensure the following rules are present:

sudo iptables -A INPUT -p udp --dport 51820 -j ACCEPT
sudo iptables -A FORWARD -i wg0 -j ACCEPT
sudo iptables -A FORWARD -o wg0 -j ACCEPT
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

To persist iptables rules across reboots, install iptables-persistent:

sudo apt install iptables-persistent -y
sudo netfilter-persistent save

Step 6: Start and Enable the WireGuard Interface

Start the WireGuard tunnel using the wg-quick utility. This tool reads the configuration file, creates the network interface, applies firewall rules, and brings the tunnel online:

sudo wg-quick up wg0

To make the tunnel start automatically on boot:

sudo systemctl enable wg-quick@wg0

Check the status of the interface at any time:

sudo wg show wg0

Expected output includes the interface's public key, listening port, and an empty peers section (until you add clients):

interface: wg0
  public key: YOUR_SERVER_PUBLIC_KEY
  private key: (hidden)
  listening port: 51820

Client Configuration

Each client that connects to your WireGuard server needs its own key pair and configuration file. The process mirrors the server setup but with the peer section pointing back to the server.

Step 1: Generate Client Keys

On the client machine, generate the key pair:

wg genkey | tee client_private.key
wg pubkey < client_private.key | tee client_public.key

If you prefer to generate keys on the server and transfer them securely to the client, you can do so, but generating them directly on the client is more secure since the private key never leaves that machine.

Step 2: Create the Client Configuration File

Create the client configuration file (on Linux, save it as /etc/wireguard/wg0.conf; on other platforms, use the WireGuard app's import function):

[Interface]
# The private key for this client
PrivateKey = YOUR_CLIENT_PRIVATE_KEY_HERE

# The IP address assigned to this client within the VPN subnet
# Use a unique address for each client (e.g., 10.0.0.2, 10.0.0.3, etc.)
Address = 10.0.0.2/24

# Optional: DNS server to use when connected to the VPN
# Uncomment and set to your preferred DNS resolver
# DNS = 1.1.1.1, 8.8.8.8

[Peer]
# The server's public key
PublicKey = YOUR_SERVER_PUBLIC_KEY_HERE

# The server's public IP address and WireGuard port
Endpoint = YOUR_SERVER_PUBLIC_IP:51820

# Allowed IPs: traffic destined for these IP ranges will be routed through the VPN
# To route ALL traffic through the VPN (full tunnel):
# AllowedIPs = 0.0.0.0/0, ::/0
# To route only VPN subnet traffic (split tunnel):
AllowedIPs = 10.0.0.0/24

# Keep the connection alive by sending a persistent keepalive packet
# Useful if the client is behind NAT or a firewall
PersistentKeepalive = 25

Replace YOUR_CLIENT_PRIVATE_KEY_HERE with the client's actual private key, YOUR_SERVER_PUBLIC_KEY_HERE with the server's public key, and YOUR_SERVER_PUBLIC_IP with the server's reachable IP address.

Step 3: Add the Client as a Peer on the Server

For the server to accept connections from this client, you must register the client's public key and assigned IP address in the server's configuration. Edit /etc/wireguard/wg0.conf on the server and append a [Peer] section:

sudo nano /etc/wireguard/wg0.conf

Add the following block at the end of the file (after the [Interface] section and its PostUp/PreDown rules):

[Peer]
# A friendly name or comment to identify this client (not parsed by WireGuard)
# Client: developer-laptop

# The client's public key
PublicKey = YOUR_CLIENT_PUBLIC_KEY_HERE

# The IP address(es) from which this peer is allowed to send traffic
# This should match the Address assigned to the client
AllowedIPs = 10.0.0.2/32

The /32 subnet mask for AllowedIPs on the server side is critical — it tells WireGuard that this specific peer is only authorized to use that single IP address. If a client is configured with AllowedIPs = 0.0.0.0/0 on its side, the server still restricts what source IPs it will accept from that peer based on this server-side AllowedIPs setting.

After editing the server configuration, reload the WireGuard interface to apply the new peer without disrupting existing connections:

sudo wg syncconf wg0 <(sudo wg-quick strip wg0)

Alternatively, you can restart the interface entirely (this will briefly interrupt all VPN traffic):

sudo wg-quick down wg0 && sudo wg-quick up wg0

Verify the peer is registered:

sudo wg show wg0

You should now see the peer listed with its public key, allowed IPs, and connection status. The "latest handshake" field will populate once the client connects.

Connecting the Client

Linux Client (using wg-quick)

On a Linux client machine with WireGuard installed, bring up the tunnel:

sudo wg-quick up wg0

To connect automatically on boot:

sudo systemctl enable wg-quick@wg0

Check connectivity by pinging the server's VPN IP:

ping 10.0.0.1

Windows, macOS, Android, and iOS Clients

Download the official WireGuard client from wireguard.com/install. Import the client configuration file directly, or copy-paste the configuration contents into the app. The tunnel can be activated with a single click or tap. The mobile apps support on-demand VPN activation for specified Wi-Fi networks and cellular data.

Testing and Troubleshooting

To confirm the VPN is functioning correctly, run these checks from the client:

# Test reachability of the server's VPN IP
ping -c 4 10.0.0.1

# Check the client's routing table — a route to the VPN subnet should appear
ip route show | grep wg0

# Verify the client's WireGuard interface status
sudo wg show wg0

On the server, check active peers and recent handshakes:

sudo wg show wg0

Look for a "latest handshake" timestamp indicating recent activity. If no handshake appears, common causes include:

Use tcpdump on the server to verify that UDP packets are arriving:

sudo tcpdump -i any udp port 51820

Adding Multiple Clients

To add more clients, repeat the client configuration steps with unique IP addresses. For example:

Each client's [Peer] block is appended to the server's wg0.conf file. After adding new peers, reload the configuration with wg syncconf as shown above.

Best Practices

Conclusion

Setting up a WireGuard VPN on Ubuntu Server is a straightforward process that yields a fast, secure, and maintainable tunnel for remote access. By following this tutorial, you have installed WireGuard, generated cryptographic key pairs, configured the server interface with NAT and firewall rules, registered client peers, and established encrypted connectivity. The simplicity of WireGuard's configuration model — a single file per node with a few clearly defined sections — makes ongoing management significantly easier than traditional VPN solutions. As you expand your deployment with additional clients, remember to follow the best practices outlined above: protect private keys rigorously, use split tunneling where possible, and keep server-side peer entries tightly scoped to specific AllowedIPs. With these principles in place, you will have a robust, production-ready VPN that serves equally well for personal privacy, remote development work, and infrastructure security.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles