How to Configure WireGuard Point-to-Site VPN Server and Peer Routing on Ubuntu Server

Legacy virtual private networking (VPN) solutions such as OpenVPN and IPsec carry substantial architectural overhead. Relying on complex certificate authorities, bulky user-space daemons, and massive codebases spanning hundreds of thousands of lines, traditional VPN protocols struggle with high packet latency, fragile connection handoffs, and cumbersome administrative maintenance.

WireGuard fundamentally modernises secure tunneling. Running directly within the Linux kernel as an ultra-lean network interface driver, WireGuard implements the modern Noise protocol framework, utilising Curve25519 for key exchange, ChaCha20-Poly1305 for authenticated encryption, and BLAKE2s for cryptographic hashing. Its revolutionary cryptokey routing model binds public encryption keys directly to permitted internal IP addresses. This guide details how to build and configure a high-performance WireGuard point-to-site VPN server, enable kernel packet forwarding, and manage client peer routing on Ubuntu Server.


Architectural Overview: Cryptokey Routing and Kernel Integration

Unlike OpenVPN, which encapsulates traffic inside a user-space daemon that repeatedly copies packets between user and kernel buffers via TUN/TAP devices, WireGuard operates entirely within kernel space:

Architectural Layer WireGuard Implementation Legacy OpenVPN / IPsec Alternative Operational Impact
Execution Context Linux Kernel Module (in-tree since 5.6) User-Space Daemon via /dev/net/tun Zero context-switching overhead; wire-speed packet throughput.
Cryptographic Primitives Fixed, modern cipher suite (Curve25519, ChaCha20, Poly1305) Negotiated OpenSSL ciphers (AES-CBC, RSA, SHA-1 legacy risks) Eliminates cipher downgrade attacks; deterministic security posture.
Access Control Cryptokey Routing Table (Public Key <=> Allowed IPs) Dynamic IP pools governed by internal routing engines Packets with forged source addresses are dropped instantly before decryption.
Connection State Silent UDP (No chatty keepalives by default) Persistent stateful TCP/UDP sessions Invisible to port scanners; instantaneous roaming across Wi-Fi and 5G.

Step 1: Installing WireGuard and Enabling Kernel Packet Forwarding

WireGuard is included directly in the standard Ubuntu repositories and Linux kernel trees:

# Update package indices and install WireGuard tooling
sudo apt update && sudo apt install -y wireguard iptables

# Verify kernel module availability
sudo modprobe wireguard && lsmod | grep wireguard

Enabling IPv4 and IPv6 Forwarding

For Ubuntu to route incoming encrypted client packets outward to the local network or public internet, enable IP packet forwarding in sysctl:

# Open sysctl configuration
sudo nano /etc/sysctl.d/99-wireguard.conf

# Add the following directives:
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

# Apply sysctl settings immediately without rebooting:
sudo sysctl --system

Step 2: Generating Server and Client Cryptographic Keypairs

WireGuard uses public-key cryptography. Both the server and each connected peer require a private key (kept secret) and a corresponding public key (shared with the remote endpoint).

# Create secure directory with restricted permissions
sudo mkdir -p /etc/wireguard
cd /etc/wireguard
sudo umask 077

# Generate Server Private and Public Keys
wg genkey | sudo tee server_private.key | wg pubkey | sudo tee server_public.key

# Generate Client 1 Private and Public Keys
wg genkey | sudo tee client1_private.key | wg pubkey | sudo tee client1_public.key

Step 3: Configuring the WireGuard Server Interface (wg0.conf)

Identify your server’s primary network interface name using ip -brief link (typically eth0, ens3, or enp1s0). In this configuration, we assume eth0.

Create the server configuration file at /etc/wireguard/wg0.conf:

[Interface]
# Server VPN address inside the encrypted tunnel
Address = 10.100.0.1/24, fd42:42:42::1/64

# Listen port for WireGuard UDP transport
ListenPort = 51820

# Server private key (read from /etc/wireguard/server_private.key)
PrivateKey = <INSERT_CONTENTS_OF_SERVER_PRIVATE_KEY>

# Automated firewall NAT routing via iptables upon startup and teardown
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

# --- Peer 1: Remote Workstation / Laptop ---
[Peer]
# Client 1 Public Key (read from /etc/wireguard/client1_public.key)
PublicKey = <INSERT_CONTENTS_OF_CLIENT1_PUBLIC_KEY>

# Fixed tunnel IP addresses allocated strictly to this peer
AllowedIPs = 10.100.0.2/32, fd42:42:42::2/128

Step 4: Configuring Ubuntu Firewall (UFW) Rules

If you run Uncomplicated Firewall (UFW) on Ubuntu, permit traffic across the WireGuard port:

# Permit WireGuard UDP ingress traffic
sudo ufw allow 51820/udp comment 'WireGuard VPN Port'

# If UFW default forward policy is DROP, allow forwarding across wg0
sudo nano /etc/default/ufw
# Change: DEFAULT_FORWARD_POLICY="ACCEPT"

# Reload UFW rules
sudo ufw reload

Step 5: Starting the WireGuard Service via Systemd

WireGuard integrates with systemd through the wg-quick helper:

# Enable and start the wg0 interface
sudo systemctl enable --now wg-quick@wg0

# Check active status and operational metrics
sudo wg show

The output displays the active interface, listening port, and registered peers waiting for an initial handshake.


Step 6: Generating Client Configuration Profiles

Create a client configuration file named client1.conf to import into the official WireGuard client for Windows, macOS, iOS, Android, or Linux:

[Interface]
# Client VPN IP address inside the tunnel
Address = 10.100.0.2/24, fd42:42:42::2/64

# Private key of this client (read from client1_private.key)
PrivateKey = <INSERT_CONTENTS_OF_CLIENT1_PRIVATE_KEY>

# Upstream DNS server (e.g. Cloudflare or internal Pi-hole)
DNS = 1.1.1.1, 1.0.0.1

[Peer]
# Server Public Key (read from server_public.key)
PublicKey = <INSERT_CONTENTS_OF_SERVER_PUBLIC_KEY>

# Public IP address or FQDN and port of the WireGuard server
Endpoint = vpn.yourdomain.com:51820

# Route all internet traffic through the VPN (Full Tunnel)
AllowedIPs = 0.0.0.0/0, ::/0

# Optional keepalive for peers located behind strict NAT firewalls
PersistentKeepalive = 25

Tip for Mobile Devices: Install qrencode on the server (sudo apt install -y qrencode) and run qrencode -t ansiutf8 < client1.conf. Scan the generated QR code directly using the mobile WireGuard app to configure the tunnel instantly.


Troubleshooting Common WireGuard VPN Faults

Observed Issue Root Cause Diagnostic & Remediation Step
wg show displays no handshake on server (0 transfer bytes) UDP port 51820 blocked by cloud security groups (AWS, Azure, DigitalOcean) or incorrect Endpoint IP Verify cloud provider firewall rules. Confirm UDP 51820 ingress is open. Test connectivity using nc -u -z -v <SERVER_IP> 51820.
Handshake succeeds, but client cannot browse the internet IP forwarding is disabled in the Linux kernel, or iptables MASQUERADE rule is misconfigured Confirm sysctl net.ipv4.ip_forward returns 1. Verify interface name in PostUp rule matches ip link (e.g., eth0 vs ens3).
Web browsing stalls on large file downloads or specific HTTPS sites Maximum Transmission Unit (MTU) packet fragmentation across the WAN connection Add MTU = 1360 under [Interface] in the client configuration to prevent carrier-level packet fragmentation over PPPoE or mobile networks.
Connection drops every few minutes when client is idle Client is behind a stateful NAT firewall that closes idle UDP port mappings Add PersistentKeepalive = 25 under [Peer] in the client configuration to maintain active firewall translation states.

Operational Governance and Security Best Practices

  • Isolate Peers via Strict Subnets: Assign distinct subnets for different functional groups (e.g., 10.100.10.0/24 for engineers, 10.100.20.0/24 for contractors) and govern cross-subnet routing using iptables filter chains.
  • Enforce Split Tunneling for Bandwidth Conservation: For enterprise deployments where users only need access to internal subnets, replace AllowedIPs = 0.0.0.0/0 with your company’s private ranges (e.g., AllowedIPs = 10.0.0.0/8, 172.16.0.0/12).
  • Automate Key Rotation via Infrastructure as Code: Store server and peer definitions in Ansible, Terraform, or Puppet to rotate keys systematically and revoke departing employees instantly by pruning peer blocks.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.