Infrastructure

WireGuard VPN — Full-Tunnel and Split-Tunnel on Ubuntu

---

WireGuard VPN — Full-Tunnel and Split-Tunnel on Ubuntu

Commercial VPNs encrypt your traffic to a server you don't control, in a country you didn't choose, with logging policies you can't verify. A self-hosted WireGuard VPN puts you in control — your server, your keys, your rules.

I run WireGuard on Ubuntu for two things: full-tunnel mode when I'm on public Wi-Fi (all traffic routes through my home server), and split-tunnel mode for accessing homelab services remotely (only internal traffic routes through the VPN). Setup takes 15 minutes and the performance is near-native — WireGuard runs in kernel space and adds ~3ms of latency.


What It Is

Protocol WireGuard (kernel-space VPN)
Purpose Encrypted remote access to your network
Platform Linux server + any client (iOS, Android, Windows, macOS)
License GPL v2
Repository wireguard.com

Why WireGuard Over OpenVPN/IPSec

PACKET ANALYSIS
WireGuard
~4,000 lines of code (auditable)
OpenVPN
~100,000 lines of code
IPSec
~400,000 lines of code (many implementations)
Performance (iperf3)
WireGuard
95% of native speed (~3ms overhead)
OpenVPN
60-80% of native speed (~10-15ms overhead)
IPSec
70-85% of native speed (~8-12ms overhead)
Handshake
~1ms (WireGuard) vs ~500ms (OpenVPN)
Roaming
Built-in (WireGuard) vs Manual (OpenVPN)

The Core Problem: Remote Access Without the Risk

Scenario Problem WireGuard Solution
Coffee shop Wi-Fi All traffic visible to network owner Full tunnel encrypts everything
Traveling Can't access homelab services Split tunnel reaches internal IPs
Remote work Need office network access Site-to-site tunnel connects offices
Mobile device Cellular data monitored by ISP Full tunnel hides all traffic
PACKET ANALYSIS
Full Tunnel
Split Tunnel

Step 0 — Server Requirements

Component Minimum
Server Ubuntu 22.04+ (any cloud VPS or homelab box)
RAM 256 MB (WireGuard is very lightweight)
CPU 1 core
Bandwidth Depends on usage (1 Mbps per connected client minimum)
Public IP Required for server (or use Dynamic DNS)

Step 1 — Install WireGuard

On Ubuntu Server

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

# Verify
wg --version
# wireguard-tools v1.0.20210914

Enable IP Forwarding

# Enable IPv4 forwarding
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

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

Step 2 — Generate Server Keys

# Generate server private key
wg genkey | sudo tee /etc/wireguard/server_private.key
sudo chmod 600 /etc/wireguard/server_private.key

# Derive public key
sudo cat /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key

# Display keys
echo "Server Private Key: $(sudo cat /etc/wireguard/server_private.key)"
echo "Server Public Key: $(sudo cat /etc/wireguard/server_public.key)"

Step 3 — Server Configuration

/etc/wireguard/wg0.conf

[Interface]
# Server settings
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>

# Firewall rules (run on interface up)
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

# Optional: DNS for clients
# DNS = 1.1.1.1, 8.8.8.8

# --- Client 1 (Phone) ---
[Peer]
PublicKey = <CLIENT1_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32

# --- Client 2 (Laptop) ---
[Peer]
PublicKey = <CLIENT2_PUBLIC_KEY>
AllowedIPs = 10.0.0.3/32

Start WireGuard

sudo wg-quick up wg0

# Enable on boot
sudo systemctl enable wg-quick@wg0

# Check status
sudo wg show
# interface: wg0
#   public key: <key>
#   private key: (hidden)
#   listening port: 51820

Step 4 — Client Configuration

Generate Client Keys

# Client 1 (Phone)
wg genkey | tee client1_private.key | wg pubkey > client1_public.key

# Client 2 (Laptop)
wg genkey | tee client2_private.key | wg pubkey > client2_public.key

Client Config — Full Tunnel

All traffic routes through the VPN (secure on public Wi-Fi):

[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 10.0.0.2/24
DNS = 1.1.1.1, 8.8.8.8

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

Client Config — Split Tunnel

Only internal traffic routes through VPN (faster for general browsing):

[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 10.0.0.2/24
DNS = 192.168.40.1  # Your Pi-hole

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = YOUR_SERVER_IP:51820
AllowedIPs = 10.0.0.0/24, 192.168.0.0/16
PersistentKeepalive = 25

QR Code for Mobile

# Install qrencode
sudo apt install qrencode -y

# Generate QR code
qrencode -t ansiutf8 < client1_full.conf
# Or save as image
qrencode -o client1.png < client1_full.conf

Step 5 — Firewall Rules

# Allow WireGuard port
sudo ufw allow 51820/udp

# Allow WireGuard traffic
sudo ufw allow in on wg0

# Allow SSH (don't lock yourself out)
sudo ufw allow ssh

# Enable UFW
sudo ufw enable
sudo ufw status

iptables (Alternative)

# Allow WireGuard port
iptables -A INPUT -p udp --dport 51820 -j ACCEPT

# Allow forwarded traffic through wg0
iptables -A FORWARD -i wg0 -j ACCEPT
iptables -A FORWARD -o wg0 -j ACCEPT

# NAT for internet access through VPN
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Step 6 — Mobile Setup

iOS/Android

  1. Install WireGuard from App Store / Play Store
  2. Open app → Add TunnelScan QR Code
  3. Scan the QR code generated in Step 4
  4. Toggle VPN on

Windows/macOS

  1. Download from wireguard.com/install
  2. Import .conf file
  3. Activate tunnel

Step 7 — Site-to-Site Tunnel

Connect two networks over WireGuard:

Site A (Home)

[Interface]
Address = 10.0.0.1/24
PrivateKey = <SITE_A_PRIVATE_KEY>
ListenPort = 51820

[Peer]
PublicKey = <SITE_B_PUBLIC_KEY>
Endpoint = SITE_B_IP:51820
AllowedIPs = 10.0.0.0/24, 192.168.20.0/24  # Site B's networks
PersistentKeepalive = 25

Site B (Office)

[Interface]
Address = 10.0.0.2/24
PrivateKey = <SITE_B_PRIVATE_KEY>
ListenPort = 51820

[Peer]
PublicKey = <SITE_A_PUBLIC_KEY>
Endpoint = SITE_A_IP:51820
AllowedIPs = 10.0.0.0/24, 192.168.10.0/24  # Site A's networks
PersistentKeepalive = 25

Step 8 — Dynamic DNS (Optional)

If your home IP changes:

# Install ddclient
sudo apt install ddclient -y

# Configure /etc/ddclient.conf
protocol=cloudflare
use=web
login=your@email.com
password=your_api_token
server=api.cloudflare.com
zone=yourdomain.com
yourdomain.com

What I'd Tell Anyone Building One

  1. Full tunnel for public Wi-Fi, split tunnel for home. When you're at a coffee shop, route everything through your VPN — the network owner can't see your traffic. When you're at home, only route internal traffic — you don't want your Netflix going through your home server in another country.

  2. WireGuard is UDP, not TCP. This matters because TCP-over-TCP causes performance problems (retransmit storms). WireGuard's UDP transport avoids this entirely. If UDP is blocked, considerOutline or Cloudflare WARP as alternatives.

  3. PersistentKeepalive = 25 is critical. Without it, the VPN tunnel drops when there's no traffic for ~2 minutes (NAT timeout). The keepalive packet every 25 seconds keeps the connection alive.

  4. Use AllowedIPs to control routing. 0.0.0.0/0 routes everything (full tunnel). 10.0.0.0/24, 192.168.0.0/16 routes only internal networks (split tunnel). This is the single most important setting in the client config.

  5. Generate a QR code for mobile. Typing 512-bit keys on a phone is error-prone. qrencode -t ansiutf8 < client.conf produces a scannable QR code that the WireGuard app imports in 2 seconds.

  6. Firewall rule: never forget SSH. Before enabling UFW, always sudo ufw allow ssh. Locking yourself out of a remote server is a rite of passage — avoid it.


Get It


Last updated: 2026-09-01 — Tested on Ubuntu 24.04 LTS with WireGuard kernel module, iOS/Android clients, site-to-site tunnels.

Comments (0)

Join the discussion — sign in to comment.

Sign in

No comments yet — be the first to share your thoughts.

Related reading