WireGuard is simple by design — a few keys, some IP addresses, and you’re connected. But “connected” and “production-ready” are different things. Intermittent disconnections, mysterious packet loss, traffic leaking outside the tunnel — these happen when you skip the fundamentals.
The two things that make WireGuard stable: correct MTU and clear routing policy. Get these right, and WireGuard becomes boring (in the best way).
WireGuard Basics on VyOS
VyOS treats WireGuard as a first-class interface. Configuration is straightforward:
configure
# Generate keypair (or use existing)run generate pki wireguard key-pair
# Save the output:# Private-key: <base64 private key># Public-key: <base64 public key>Store the private key securely. The public key is what you share with peers.
Site-to-Site Configuration
Two VyOS routers connecting their networks. Site A (10.1.0.0/24) connects to Site B (10.2.0.0/24).
Site A Configuration
configure
# WireGuard interfaceset interfaces wireguard wg0 address '10.255.255.1/30'set interfaces wireguard wg0 description 'Site-to-Site to Site B'set interfaces wireguard wg0 port '51820'set interfaces wireguard wg0 private-key '<site-a-private-key>'
# Peer configuration (Site B)set interfaces wireguard wg0 peer site-b public-key '<site-b-public-key>'set interfaces wireguard wg0 peer site-b allowed-ips '10.255.255.2/32'set interfaces wireguard wg0 peer site-b allowed-ips '10.2.0.0/24'set interfaces wireguard wg0 peer site-b endpoint 'site-b.example.com:51820'set interfaces wireguard wg0 peer site-b persistent-keepalive '25'
# Route to Site B's networkset protocols static route 10.2.0.0/24 interface wg0
# Firewall: allow WireGuard trafficset firewall ipv4 name WAN-LOCAL rule 60 action 'accept'set firewall ipv4 name WAN-LOCAL rule 60 protocol 'udp'set firewall ipv4 name WAN-LOCAL rule 60 destination port '51820'
commitSite B Configuration
Mirror configuration with swapped keys and addresses:
configure
set interfaces wireguard wg0 address '10.255.255.2/30'set interfaces wireguard wg0 description 'Site-to-Site to Site A'set interfaces wireguard wg0 port '51820'set interfaces wireguard wg0 private-key '<site-b-private-key>'
set interfaces wireguard wg0 peer site-a public-key '<site-a-public-key>'set interfaces wireguard wg0 peer site-a allowed-ips '10.255.255.1/32'set interfaces wireguard wg0 peer site-a allowed-ips '10.1.0.0/24'set interfaces wireguard wg0 peer site-a endpoint 'site-a.example.com:51820'set interfaces wireguard wg0 peer site-a persistent-keepalive '25'
set protocols static route 10.1.0.0/24 interface wg0
set firewall ipv4 name WAN-LOCAL rule 60 action 'accept'set firewall ipv4 name WAN-LOCAL rule 60 protocol 'udp'set firewall ipv4 name WAN-LOCAL rule 60 destination port '51820'
commitValidation:
# Check interface statusshow interfaces wireguard wg0
# Check peer statusshow wireguard peers
# Test connectivityping 10.255.255.2 # Tunnel endpointping 10.2.0.1 # Remote LAN (from Site A)Road Warrior Configuration (Mobile Clients)
Roaming clients that connect from anywhere. The VyOS router acts as the VPN server.
VyOS Server Configuration
configure
set interfaces wireguard wg0 address '10.10.0.1/24'set interfaces wireguard wg0 description 'Road Warrior VPN'set interfaces wireguard wg0 port '51820'set interfaces wireguard wg0 private-key '<server-private-key>'
# Client 1 (laptop)set interfaces wireguard wg0 peer laptop public-key '<laptop-public-key>'set interfaces wireguard wg0 peer laptop allowed-ips '10.10.0.10/32'
# Client 2 (phone)set interfaces wireguard wg0 peer phone public-key '<phone-public-key>'set interfaces wireguard wg0 peer phone allowed-ips '10.10.0.11/32'
# Allow VPN clients to access LAN and internetset firewall group network-group VPN-CLIENTS network '10.10.0.0/24'
# NAT for VPN clients going to internetset nat source rule 200 outbound-interface name 'eth0'set nat source rule 200 source address '10.10.0.0/24'set nat source rule 200 translation address 'masquerade'
# Firewall: allow WireGuardset firewall ipv4 name WAN-LOCAL rule 60 action 'accept'set firewall ipv4 name WAN-LOCAL rule 60 protocol 'udp'set firewall ipv4 name WAN-LOCAL rule 60 destination port '51820'
commitClient Configuration (wg0.conf)
For laptop/phone using standard WireGuard client:
[Interface]PrivateKey = <laptop-private-key>Address = 10.10.0.10/32DNS = 10.0.0.1
[Peer]PublicKey = <server-public-key>AllowedIPs = 0.0.0.0/0Endpoint = vpn.example.com:51820PersistentKeepalive = 25AllowedIPs = 0.0.0.0/0 means full tunnel — all traffic through VPN.
Split Tunnel vs Full Tunnel
Full tunnel: All client traffic goes through VPN
AllowedIPs = 0.0.0.0/0, ::/0- Pros: All traffic protected, consistent IP
- Cons: Higher latency, more bandwidth on VPN server
Split tunnel: Only specific traffic through VPN
AllowedIPs = 10.0.0.0/8, 192.168.0.0/16- Pros: Better performance, less server load
- Cons: Some traffic exposed, DNS leaks possible
Split Tunnel Client Example
[Interface]PrivateKey = <private-key>Address = 10.10.0.10/32
[Peer]PublicKey = <server-public-key>AllowedIPs = 10.0.0.0/8, 10.10.0.0/24Endpoint = vpn.example.com:51820PersistentKeepalive = 25Only traffic to 10.x.x.x goes through VPN. Everything else uses local internet.
The MTU Problem
WireGuard encapsulates packets, adding overhead. If your MTU is too high, packets get fragmented or dropped. Symptoms:
- SSH works, HTTPS fails
- Small requests work, large transfers hang
- Intermittent “connection reset”
Calculate Correct MTU
Standard Ethernet MTU: 1500 WireGuard overhead: 60 bytes (IPv4) or 80 bytes (IPv6) Safe WireGuard MTU: 1420 (IPv4) or 1400 (IPv6)
configure
set interfaces wireguard wg0 mtu '1420'
commitTest MTU
# From client, test path MTU to a host through the tunnelping -M do -s 1392 10.0.0.1-M do prevents fragmentation. -s 1392 = 1392 payload + 28 header = 1420. If it works, MTU is correct. If not, lower it.
For connections through multiple NATs or tunnels, you might need 1380 or even lower.
Kill Switch: Preventing Leaks
A kill switch ensures traffic can’t leak if the VPN disconnects. On VyOS server, you control routing. On clients, configure the client app or OS firewall.
Server-Side: Ensure Clients Use VPN
If clients should only access internet through VPN:
# Already covered by NAT rule - VPN clients are masqueraded# No direct route from VPN subnet to internet except through NATClient-Side Kill Switch (Linux)
# Allow only WireGuard and local trafficiptables -A OUTPUT -o wg0 -j ACCEPTiptables -A OUTPUT -o lo -j ACCEPTiptables -A OUTPUT -p udp --dport 51820 -j ACCEPTiptables -A OUTPUT -j DROPOr use WireGuard’s PostUp/PostDown scripts in the config.
Persistent Keepalive: When to Use It
WireGuard is silent when idle — no traffic means no packets. This causes problems:
- NAT mappings expire (typically 30-60 seconds)
- Stateful firewalls drop the “connection”
- You can’t initiate connections TO the client
persistent-keepalive '25' sends a keepalive every 25 seconds, keeping NAT/firewall state alive.
Use it when:
- Client is behind NAT
- Either side has stateful firewall
- You need to reach the client from the server
Skip it when:
- Both sides have static public IPs
- No NAT involved
- Saving minimal bandwidth matters
Debugging WireGuard
Check Interface Status
show interfaces wireguard wg0Should show UP state and assigned address.
Check Peer Handshakes
show wireguard peersShows last handshake time. If “never” or very old, tunnel isn’t working.
Check Keys Match
Most common issue: public/private key mismatch. Verify:
- Server has client’s public key
- Client has server’s public key
- No copy-paste errors (check for trailing spaces)
Check Firewall
show firewall ipv4 name WAN-LOCALEnsure UDP 51820 is allowed inbound.
Check Routing
show ip routeRoutes through wg0 should exist for peer’s allowed-ips.
Monitor Traffic
sudo tcpdump -i wg0 -nShould see traffic when peers communicate.
Common Issues
| Symptom | Cause | Fix |
|---|---|---|
| No handshake | Key mismatch or blocked port | Verify keys, check firewall |
| Handshake but no traffic | Routing or allowed-ips wrong | Check routes match allowed-ips |
| Works then dies | NAT timeout | Enable persistent-keepalive |
| Large transfers fail | MTU too high | Lower MTU to 1420 or less |
| One direction works | Asymmetric allowed-ips | Both sides need matching allowed-ips |
Production Checklist
Before calling it production-ready:
- MTU set correctly (1420 or tested value)
- Persistent keepalive enabled if behind NAT
- Firewall allows WireGuard port (UDP 51820)
- Routes exist for all allowed-ips
- Keys are backed up securely
- NAT configured if clients need internet access
- DNS configured for full-tunnel clients
- Kill switch configured if leak prevention needed
Complete Road Warrior Example
# === WireGuard Interface ===set interfaces wireguard wg0 address '10.10.0.1/24'set interfaces wireguard wg0 mtu '1420'set interfaces wireguard wg0 port '51820'set interfaces wireguard wg0 private-key '<server-private-key>'
# === Peers ===set interfaces wireguard wg0 peer laptop public-key '<key>'set interfaces wireguard wg0 peer laptop allowed-ips '10.10.0.10/32'
set interfaces wireguard wg0 peer phone public-key '<key>'set interfaces wireguard wg0 peer phone allowed-ips '10.10.0.11/32'
# === NAT for VPN clients ===set nat source rule 200 outbound-interface name 'eth0'set nat source rule 200 source address '10.10.0.0/24'set nat source rule 200 translation address 'masquerade'
# === Firewall ===set firewall ipv4 name WAN-LOCAL rule 60 action 'accept'set firewall ipv4 name WAN-LOCAL rule 60 protocol 'udp'set firewall ipv4 name WAN-LOCAL rule 60 destination port '51820'
# VPN clients to LAN (apply via forward filter)set firewall ipv4 name VPN-TO-LAN default-action 'accept'set firewall ipv4 forward filter rule 50 inbound-interface name 'wg0'set firewall ipv4 forward filter rule 50 action 'jump'set firewall ipv4 forward filter rule 50 jump-target 'VPN-TO-LAN'
# === DNS for VPN clients ===set service dns forwarding listen-address '10.10.0.1'set service dns forwarding allow-from '10.10.0.0/24'The Lesson
WireGuard becomes stable after addressing two things:
-
MTU: Set it explicitly to 1420 or lower. Don’t rely on automatic MTU discovery — it often fails through NAT and firewalls.
-
Routing policy: Be explicit about what traffic goes where.
allowed-ipscontrols both routing AND cryptographic acceptance. If it’s not in allowed-ips, it won’t be accepted even if routed correctly.
Everything else — keepalives, firewall rules, NAT — follows logically from the use case. But MTU and routing policy are where most WireGuard problems live. Fix those, and the rest falls into place.