Skip to main content
COSMICBYTEZLABS
NewsSecurityHOWTOsToolsStudyTraining
ProjectsChecklistsAI RankingsNewsletterStatusTagsAbout
Subscribe

Press Enter to search or Esc to close

News
Security
HOWTOs
Tools
Study
Training
Projects
Checklists
AI Rankings
Newsletter
Status
Tags
About
RSS Feed
Reading List
Subscribe

Stay in the Loop

Get the latest security alerts, tutorials, and tech insights delivered to your inbox.

Subscribe NowFree forever. No spam.
COSMICBYTEZLABS

Your trusted source for IT intelligence, cybersecurity insights, and hands-on technical guides.

494+ Articles
116+ Guides

CONTENT

  • Latest News
  • Security Alerts
  • HOWTOs
  • Projects
  • Exam Prep

RESOURCES

  • Search
  • Browse Tags
  • Newsletter Archive
  • Reading List
  • RSS Feed

COMPANY

  • About Us
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CosmicBytez Labs. All rights reserved.

System Status: Operational
  1. Home
  2. Projects
  3. WireGuard Road Warrior VPN Server
WireGuard Road Warrior VPN Server
PROJECTIntermediate

WireGuard Road Warrior VPN Server

Build a self-hosted WireGuard VPN server on Ubuntu for secure remote access — with NAT masquerading, DNS leak protection, QR-code client provisioning, and a kill switch.

Dylan H.

Projects

April 1, 2026
7 min read
2-3 hours

Tools & Technologies

WireGuardwg-quickiptablesqrencodesystemdufw

Overview

WireGuard is a modern, kernel-level VPN that replaces legacy solutions like OpenVPN and IPsec with a dramatically simpler design. A single UDP port, ~4,000 lines of auditable code, and ChaCha20-Poly1305 encryption give you a fast, low-latency tunnel that is easy to reason about and harden.

This project walks through the "road warrior" pattern — one always-on server, many roaming clients. You will end up with:

  • A fully routed WireGuard server that forwards all client traffic (full-tunnel mode)
  • Per-client key pairs provisioned as terminal QR codes for instant mobile import
  • DNS leak protection via a VPN-side resolver
  • A UFW-based firewall with a kill-switch profile
  • Systemd auto-start with wg-quick

Target platform is Ubuntu Server 22.04 / 24.04 (works on any Debian-based distro). The server can be a VPS, a home server behind a static IP, or a homelab VM exposed via port forwarding.


Architecture

[Laptop / Phone]                [Ubuntu VPN Server]            [Internet]
  wg0: 10.10.0.2/32  ──UDP──►  wg0: 10.10.0.1/24  ──NAT──►  8.8.8.8
  wg0: 10.10.0.3/32  ──UDP──►  eth0: <public IP>

All client traffic is encapsulated in WireGuard UDP datagrams aimed at port 51820 on the server. The server uses iptables MASQUERADE to NAT that traffic out through its WAN interface (eth0 / ens3 — varies by host). From the internet's perspective, all client requests appear to originate from the server's public IP.

Key design decisions:

DecisionChoiceWhy
Routing modeFull tunnel (AllowedIPs = 0.0.0.0/0)Prevent traffic leaks outside VPN
DNSCloudflare 1.1.1.1 via VPN interfaceStop DNS leaks to local ISP
Key authPer-client Curve25519 + optional PSKPost-quantum forward secrecy
FirewallUFW + iptables PostUp/PostDownAuto-clean on interface down
ProvisioningQR codes via qrencode10-second mobile onboarding

Prerequisites

  • Ubuntu 22.04 or 24.04 server with a public IPv4 address (VPS or home server + port forwarding)
  • Root or sudo access
  • Port 51820/UDP open inbound in your cloud provider security group or home router
  • A domain or static IP you can hand to clients

Check your WAN interface name — it varies by provider:

ip -o -4 route show to default | awk '{print $5}'
# common values: eth0, ens3, enp3s0, ens160

Save the result — you will use it in the PostUp/PostDown rules.


Step 1 — Install WireGuard

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

WireGuard is in the mainline kernel since 5.6, so no DKMS module is needed on Ubuntu 22.04+. Verify the module loads:

sudo modprobe wireguard && echo "wireguard module OK"

Step 2 — Generate Server Keys

Work inside /etc/wireguard/ and lock down permissions immediately:

cd /etc/wireguard
sudo umask 077
 
# Generate private key, derive public key
sudo wg genkey | sudo tee server_private.key | sudo wg pubkey | sudo tee server_public.key
 
# Verify
sudo cat server_public.key   # base64 string ~44 chars

Keep server_private.key secret — it never leaves the server.


Step 3 — Configure the Server Interface

Create /etc/wireguard/wg0.conf. Replace <SERVER_PRIVATE_KEY> with the contents of server_private.key and WAN_IFACE with your interface name (e.g. eth0):

[Interface]
PrivateKey = <SERVER_PRIVATE_KEY>
Address    = 10.10.0.1/24
ListenPort = 51820
DNS        = 1.1.1.1
 
# NAT masquerading — swap eth0 for your WAN interface
PostUp   = iptables -A FORWARD -i %i -j ACCEPT; \
           iptables -A FORWARD -o %i -j ACCEPT; \
           iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; \
           iptables -D FORWARD -o %i -j ACCEPT; \
           iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
 
# Peers are added below — one [Peer] block per client

Lock down the config file:

sudo chmod 600 /etc/wireguard/wg0.conf
sudo chmod 600 /etc/wireguard/server_private.key

Step 4 — Enable IP Forwarding

WireGuard forwards packets between the VPN subnet and the internet. IP forwarding must be enabled at the kernel level:

# Persist across reboots
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
echo "net.ipv6.conf.all.forwarding = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Verify:

sysctl net.ipv4.ip_forward
# net.ipv4.ip_forward = 1

Step 5 — Configure UFW Firewall

Allow WireGuard and SSH before enabling the firewall:

sudo ufw allow 22/tcp       # SSH — do NOT skip this step
sudo ufw allow 51820/udp    # WireGuard
 
# UFW's DEFAULT_FORWARD_POLICY blocks forwarded packets
sudo sed -i 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' \
  /etc/default/ufw
 
sudo ufw enable
sudo ufw status verbose

Step 6 — Start the WireGuard Service

sudo systemctl enable --now wg-quick@wg0
 
# Confirm it is running
sudo systemctl status wg-quick@wg0
sudo wg show

wg show should display the wg0 interface with your server public key and listening port. No peers yet — that comes next.


Step 7 — Add Client Peers

Repeat this block for every device you want to connect. Replace <N> with an incrementing number starting at 2 (e.g. 10.10.0.2, 10.10.0.3, …).

7a — Generate client keys on the server

cd /etc/wireguard
sudo wg genkey | sudo tee client<N>_private.key | sudo wg pubkey | sudo tee client<N>_public.key
 
# Optional: pre-shared key for extra layer of symmetric encryption
sudo wg genpsk | sudo tee client<N>_psk.key

7b — Append a [Peer] block to wg0.conf

[Peer]
# Client N — <device description>
PublicKey    = <CLIENT_N_PUBLIC_KEY>
PresharedKey = <CLIENT_N_PSK>        # remove line if not using PSK
AllowedIPs   = 10.10.0.<N>/32

Hot-reload the config without dropping existing sessions:

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

7c — Build the client config file

Create /etc/wireguard/client<N>.conf:

[Interface]
PrivateKey = <CLIENT_N_PRIVATE_KEY>
Address    = 10.10.0.<N>/32
DNS        = 1.1.1.1
 
[Peer]
PublicKey           = <SERVER_PUBLIC_KEY>
PresharedKey        = <CLIENT_N_PSK>
Endpoint            = <SERVER_PUBLIC_IP>:51820
AllowedIPs          = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

AllowedIPs = 0.0.0.0/0, ::/0 routes all IPv4 and IPv6 traffic through the VPN (full-tunnel / kill-switch behaviour on most WireGuard clients).

PersistentKeepalive = 25 keeps the NAT mapping alive when the client is behind a firewall that closes idle UDP sessions.


Step 8 — Provision Clients via QR Code

For iOS and Android, the WireGuard app can import a config by scanning a QR code — no file transfer needed:

# Display QR in the terminal (scan with WireGuard app)
sudo qrencode -t ansiutf8 < /etc/wireguard/client<N>.conf

For desktop clients:

  • Linux: copy client<N>.conf to /etc/wireguard/ and run sudo wg-quick up client<N>
  • Windows/macOS: import via the WireGuard GUI (File → Import tunnel from file)
  • Android/iOS: scan the QR code in the WireGuard app

Testing

From the client

# Connect (Linux)
sudo wg-quick up client<N>
 
# Verify your public IP changed
curl https://ifconfig.me
 
# Ping the server's VPN IP
ping 10.10.0.1
 
# Check for DNS leaks
# Visit https://dnsleaktest.com — all resolvers should be Cloudflare or your chosen DNS

From the server

# See connected peers, handshake timestamps, data transfer
sudo wg show
 
# Sample output:
# peer: <client public key>
#   endpoint: <client IP>:<port>
#   allowed ips: 10.10.0.2/32
#   latest handshake: 12 seconds ago
#   transfer: 1.23 MiB received, 456 KiB sent

A "latest handshake" within the last few minutes confirms the tunnel is active and data is flowing.

Kill-switch test

  1. Connect the WireGuard client with AllowedIPs = 0.0.0.0/0.
  2. While connected, disable the WireGuard interface on the client (sudo wg-quick down client<N>).
  3. Attempt to reach the internet — most WireGuard clients will block traffic until the tunnel is restored.

Hardening Checklist

# 1. Rotate keys if a device is lost
#    Remove the [Peer] block from wg0.conf and sync:
sudo wg set wg0 peer <OLD_PUBLIC_KEY> remove
sudo wg syncconf wg0 <(sudo wg-quick strip wg0)
# Then delete the old key files
 
# 2. Restrict SSH to VPN subnet only (optional, advanced)
sudo ufw delete allow 22/tcp
sudo ufw allow from 10.10.0.0/24 to any port 22
 
# 3. Fail2ban for SSH brute-force (WireGuard itself uses PKI — no brute-force surface)
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
 
# 4. Automatic security updates
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Deployment

This setup runs cleanly on any of the following:

PlatformNotes
VPS (Hetzner, DigitalOcean, Vultr)Open port 51820/UDP in provider firewall
Home server (behind router)Port forward 51820/UDP → server LAN IP
Proxmox VMUse virtio NIC; enable IP forwarding in VM
LXC containerPrivileged container required; add lxc.cap.drop = workaround
Raspberry Pi 4Excellent low-power option; Ubuntu Server 22.04 arm64

For a VPS deployment, a $5/month Hetzner CX11 or DigitalOcean Droplet handles dozens of concurrent clients without breaking a sweat — WireGuard's kernel-space implementation is extremely efficient.


Extensions and Next Steps

Multi-server mesh (site-to-site): Add a [Peer] block on each server pointing to the other, with AllowedIPs set to the remote LAN subnet. This turns your road-warrior setup into a full mesh between your home network and a remote datacenter.

Internal DNS with AdGuard Home or Pi-hole: Replace DNS = 1.1.1.1 with the VPN IP of a Pi-hole container (10.10.0.100). All VPN clients get ad-blocking at the DNS layer with zero client config changes.

WireGuard UI (wg-easy): A Docker-based web UI that manages peers, QR codes, and bandwidth graphs without touching config files:

docker run -d \
  --name wg-easy \
  -e WG_HOST=<your-server-ip> \
  -e PASSWORD=<admin-password> \
  -v ~/.wg-easy:/etc/wireguard \
  -p 51820:51820/udp \
  -p 51821:51821/tcp \
  --cap-add=NET_ADMIN \
  --cap-add=SYS_MODULE \
  --sysctl="net.ipv4.conf.all.src_valid_mark=1" \
  --sysctl="net.ipv4.ip_forward=1" \
  --restart unless-stopped \
  ghcr.io/wg-easy/wg-easy

Monitoring: Export WireGuard peer stats to Prometheus using prometheus-wireguard-exporter, then visualise handshake latency and per-peer bandwidth in Grafana.

IPv6 dual-stack: Add an fd10::/64 ULA subnet alongside 10.10.0.0/24 and configure Address = fd10::1/64 on the server interface. Clients receive both IPv4 and IPv6 addresses, preventing IPv6 leaks common on single-stack VPN setups.

#vpn#wireguard#networking#privacy#linux#homelab#security

Related Articles

Build a Collaborative IPS with CrowdSec

Deploy CrowdSec on a Linux server to get community-powered intrusion prevention — block brute-force attacks, credential stuffing, and vulnerability...

10 min read

Keycloak SSO: Self-Hosted Identity Provider for Your Homelab

Deploy Keycloak with Docker Compose and PostgreSQL to build a centralised single sign-on platform for your homelab services, with OIDC integration for...

11 min read

HashiCorp Vault: Secrets Management for Your Homelab and

Deploy HashiCorp Vault to centrally manage secrets, certificates, and dynamic credentials — eliminating hardcoded passwords from your infrastructure with...

12 min read
Back to all Projects