WireGuard Tunnel
Encrypt all traffic between your shield VPS and origin server with WireGuard. Complete privacy and enhanced origin IP protection guide.
Overview
By default, traffic between your shield VPS and your origin server travels over the public internet. While Lumos Gate hides your origin IP from external attackers by proxying all traffic through the shield, the traffic itself between shield and origin is not encrypted at the network level (unless your origin serves HTTPS and SSL/TLS is configured end-to-end). This means your VPS provider, or anyone with access to the network path, could theoretically inspect the traffic between the shield and origin.
WireGuard solves this by creating an encrypted tunnel between the two servers. All traffic flows through this tunnel, ensuring complete privacy and adding another layer of origin IP protection.
Why Use WireGuard
-
End-to-end encryption. All traffic between shield and origin is encrypted with modern cryptography (ChaCha20, Poly1305). Even if someone has access to the network infrastructure -- including your VPS provider at the hypervisor level -- they cannot read the traffic.
-
Origin IP invisibility. Without WireGuard, your VPS provider can see the destination IP of outgoing connections from the shield VPS, which reveals your origin IP. With WireGuard, traffic goes to a local tunnel interface (10.0.0.x), and the origin's public IP is only known to the WireGuard configuration on the shield VPS.
-
Minimal performance overhead. WireGuard is built into the Linux kernel (since version 5.6) and adds approximately 3% CPU overhead. It is significantly faster than IPsec or OpenVPN, handling multi-gigabit throughput on modern hardware.
-
Simple configuration. WireGuard uses a single configuration file per server with just a handful of settings. No certificates, no complex PKI, no connection negotiation protocols.
-
Jurisdiction isolation. By placing your shield VPS and origin server in different countries and connecting them via WireGuard, you create strong jurisdictional separation. No single provider or authority has access to both the traffic content and the origin server location.
-
Stronger firewall rules. When combined with an origin firewall, you can restrict origin access to WireGuard tunnel IPs (private 10.x.x.x addresses) which cannot be spoofed from the public internet, making the firewall rules more secure than IP-based allow rules.
Prerequisites
Before starting, ensure you have:
- Linux kernel 5.6+ on both servers (for built-in WireGuard support). Check with:
uname -r - Root/sudo access on both the shield VPS and origin server
- UDP port 51820 open on the shield VPS firewall (for incoming WireGuard connections)
- A working Lumos Gate setup with at least one shield server and one domain configured
Note: All supported distributions (Debian 12+ and Ubuntu 24.04+) include WireGuard support out of the box with built-in kernel modules. See Supported OS for the full compatibility list.
Network Diagram
Before diving into the setup, here is what the network looks like with and without WireGuard:
WITHOUT WireGuard:
Internet -> Shield VPS (HAProxy) --[public internet, unencrypted]--> Origin (public IP visible to VPS provider)
WITH WireGuard:
Internet -> Shield VPS (HAProxy) --[encrypted WireGuard tunnel]--> Origin (only tunnel IP 10.0.0.2 visible)
10.0.0.1 10.0.0.2The shield VPS acts as the WireGuard "server" (listens on UDP port 51820), and the origin server connects to it as a "client" (initiates the connection with a keepalive).
Step-by-Step Setup
Step 1: Install WireGuard on Both Servers
Install WireGuard on both the shield VPS and the origin server:
Debian / Ubuntu:
sudo apt update && sudo apt install wireguard -yVerify installation on both servers:
wg --versionExpected output:
wireguard-tools v1.0.20210914 - https://git.zx2c4.com/wireguard-tools/Step 2: Generate Keys on Both Servers
Generate a private and public key pair on each server. Every WireGuard peer needs its own unique key pair.
On the shield VPS:
# Generate keys
wg genkey | sudo tee /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key
# Secure the private key (critical -- must not be world-readable)
sudo chmod 600 /etc/wireguard/private.key
# Display the public key -- copy this, you will need it for the origin server config
sudo cat /etc/wireguard/public.keyExample output:
aB3dEfGhIjKlMnOpQrStUvWxYz0123456789abcde=On the origin server:
# Generate keys
wg genkey | sudo tee /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key
# Secure the private key
sudo chmod 600 /etc/wireguard/private.key
# Display the public key -- copy this, you will need it for the shield VPS config
sudo cat /etc/wireguard/public.keyExample output:
xY9zAbCdEfGhIjKlMnOpQrStUvWxYz0123456789ab=Save both public keys. You will need each server's public key for the other server's configuration:
| Server | Public Key (example) | Used In |
|---|---|---|
| Shield VPS | aB3dEfGh...abcde= | Origin server's [Peer] section |
| Origin Server | xY9zAbCd...9ab= | Shield VPS's [Peer] section |
Warning: Never share or expose private keys. The private key must stay on the server that generated it. Only the public key is shared between peers.
Step 3: Configure the Shield VPS (WireGuard Server)
Create the WireGuard configuration on your shield VPS. The shield VPS acts as the "server" -- it listens on a fixed UDP port for incoming connections.
sudo nano /etc/wireguard/wg0.confPaste the following configuration:
[Interface]
# Shield VPS tunnel IP -- this becomes the "source IP" for traffic to the origin
Address = 10.0.0.1/24
# Shield VPS private key (from Step 2)
PrivateKey = SHIELD_PRIVATE_KEY_HERE
# WireGuard listen port (UDP)
ListenPort = 51820
[Peer]
# Origin server public key (from Step 2)
PublicKey = ORIGIN_PUBLIC_KEY_HERE
# Only allow traffic from the origin's tunnel IP
AllowedIPs = 10.0.0.2/32Replace the placeholder values:
SHIELD_PRIVATE_KEY_HERE-- Contents of/etc/wireguard/private.keyon the shield VPSORIGIN_PUBLIC_KEY_HERE-- The public key you copied from the origin server in Step 2
Secure the configuration file:
sudo chmod 600 /etc/wireguard/wg0.confOpen UDP port 51820 on the shield VPS firewall:
# UFW
sudo ufw allow 51820/udp
# Or iptables
sudo iptables -A INPUT -p udp --dport 51820 -j ACCEPTNote: The Lumos agent install script includes edge-setup.sh which tunes the VPS kernel for high performance. This does not conflict with WireGuard. However, if you have custom firewall rules, ensure UDP port 51820 is not blocked.
Step 4: Configure the Origin Server (WireGuard Client)
Create the WireGuard configuration on your origin server. The origin acts as the "client" -- it initiates the connection to the shield VPS.
sudo nano /etc/wireguard/wg0.confPaste the following configuration:
[Interface]
# Origin server tunnel IP
Address = 10.0.0.2/24
# Origin server private key (from Step 2)
PrivateKey = ORIGIN_PRIVATE_KEY_HERE
[Peer]
# Shield VPS public key (from Step 2)
PublicKey = SHIELD_PUBLIC_KEY_HERE
# Shield VPS public IP and WireGuard port -- replace with your actual shield VPS IP
Endpoint = 203.0.113.10:51820
# Only allow traffic from the shield's tunnel IP
AllowedIPs = 10.0.0.1/32
# Keep the connection alive (essential when origin is behind NAT or stateful firewall)
PersistentKeepalive = 25Replace the placeholder values:
ORIGIN_PRIVATE_KEY_HERE-- Contents of/etc/wireguard/private.keyon the origin serverSHIELD_PUBLIC_KEY_HERE-- The public key you copied from the shield VPS in Step 2203.0.113.10-- Your shield VPS's actual public IP address
Secure the configuration file:
sudo chmod 600 /etc/wireguard/wg0.confImportant: The
PersistentKeepalive = 25setting sends a keepalive packet every 25 seconds. This is essential when the origin server is behind NAT or a stateful firewall, as it keeps the UDP connection state alive. Without this, the NAT mapping may expire after a period of inactivity, causing the tunnel to stop working. Even if your origin is not behind NAT, this setting is harmless and recommended.
Step 5: Start WireGuard on Both Servers
Start WireGuard and enable it to start automatically on boot. Start the shield VPS first (since it is the "server" that listens for connections), then start the origin server.
On the shield VPS:
# Start WireGuard and enable on boot
sudo systemctl enable --now wg-quick@wg0
# Verify the interface is up
sudo wg showExpected output:
interface: wg0
public key: aB3dEfGhIjKlMnOpQrStUvWxYz0123456789abcde=
private key: (hidden)
listening port: 51820On the origin server:
# Start WireGuard and enable on boot
sudo systemctl enable --now wg-quick@wg0
# Verify the interface is up and connected
sudo wg showExpected output (after connection is established):
interface: wg0
public key: xY9zAbCdEfGhIjKlMnOpQrStUvWxYz0123456789ab=
private key: (hidden)
listening port: 43210
peer: aB3dEfGhIjKlMnOpQrStUvWxYz0123456789abcde=
endpoint: 203.0.113.10:51820
allowed ips: 10.0.0.1/32
latest handshake: 5 seconds ago
transfer: 1.24 KiB received, 1.08 KiB sent
persistent keepalive: every 25 secondsThe key indicator of a working connection is latest handshake: X seconds ago. If this field is missing, the peers have not successfully connected yet.
Step 6: Test the Tunnel
Verify connectivity through the tunnel from both sides:
From the shield VPS, ping the origin:
ping -c 4 10.0.0.2Expected output:
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=12.3 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=11.8 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=12.1 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=11.9 msFrom the origin, ping the shield:
ping -c 4 10.0.0.1Test HTTP connectivity (if your origin serves web traffic):
# From the shield VPS, test HTTP through the tunnel
curl -v http://10.0.0.2If pings succeed but HTTP fails, your origin server may have a firewall rule that only allows HTTP on specific interfaces. See the troubleshooting section below.
Step 7: Update Lumos Dashboard
Now that the WireGuard tunnel is active, update your domain configuration in Lumos to use the tunnel IP instead of the origin's public IP:
- Go to Dashboard -> Domains.
- Select your domain.
- Edit the origin server address.
- Change the origin IP from the public IP (e.g.,
198.51.100.50) to the WireGuard tunnel IP:10.0.0.2. - Save the changes.
HAProxy will now forward traffic to 10.0.0.2, which routes through the encrypted WireGuard tunnel to your origin server.
Note: Lumos Gate explicitly allows private/tunnel IPs as valid origin addresses. WireGuard addresses (10.x.x.x), Docker networks (172.x.x.x), and localhost (127.x.x.x) are all accepted. Only non-routable addresses like 0.0.0.0 and link-local (169.254.x.x, fe80::) are rejected.
Step 8: Lock Down the Origin Firewall
This is a critical step that is often overlooked. Now that traffic flows through the WireGuard tunnel, configure your origin server's firewall to only accept web traffic from the tunnel IP, not from the public internet:
# On the origin server:
# Allow SSH (do this FIRST to avoid lockout)
sudo ufw allow 22/tcp
# Allow HTTP/HTTPS only from WireGuard tunnel
sudo ufw allow from 10.0.0.1 to any port 80 proto tcp
sudo ufw allow from 10.0.0.1 to any port 443 proto tcp
# Allow WireGuard traffic (so the tunnel itself works)
sudo ufw allow 51820/udp
# Block all other HTTP/HTTPS traffic
sudo ufw deny 80/tcp
sudo ufw deny 443/tcp
# Enable the firewall
sudo ufw enableThis is more secure than allowing the shield VPS's public IP because:
- The tunnel IP
10.0.0.1is a private address that cannot be spoofed from the public internet. - Even if someone discovers both the shield VPS's public IP and your origin's IP, they cannot send traffic that appears to come from
10.0.0.1.
See Origin Firewall for detailed firewall configuration including iptables alternatives, verification steps, and other ports to consider.
Multiple Origin Servers
If you have multiple origin servers behind the same shield VPS, assign each one a unique tunnel IP:
| Server | Tunnel IP | Role |
|---|---|---|
| Shield VPS | 10.0.0.1 | Proxy / WAF |
| Origin Server 1 | 10.0.0.2 | Web application |
| Origin Server 2 | 10.0.0.3 | API server |
| Origin Server 3 | 10.0.0.4 | Database frontend |
Add a [Peer] block for each origin server in the shield VPS configuration:
[Interface]
Address = 10.0.0.1/24
PrivateKey = SHIELD_PRIVATE_KEY_HERE
ListenPort = 51820
[Peer]
# Origin Server 1 (web app)
PublicKey = ORIGIN1_PUBLIC_KEY
AllowedIPs = 10.0.0.2/32
[Peer]
# Origin Server 2 (API)
PublicKey = ORIGIN2_PUBLIC_KEY
AllowedIPs = 10.0.0.3/32
[Peer]
# Origin Server 3 (DB frontend)
PublicKey = ORIGIN3_PUBLIC_KEY
AllowedIPs = 10.0.0.4/32Each origin server gets its own standard config pointing to the shield VPS, with its own unique Address (10.0.0.2, 10.0.0.3, etc.).
In the Lumos dashboard, set each domain's origin to the appropriate tunnel IP (e.g., app.example.com -> 10.0.0.2, api.example.com -> 10.0.0.3).
Multiple Shield Servers
If a domain is assigned to multiple shield VPS servers for failover, set up a WireGuard tunnel from each shield server to the origin. Use separate tunnel IPs for each shield:
# On the origin server -- multiple peers
[Interface]
Address = 10.0.0.2/24
PrivateKey = ORIGIN_PRIVATE_KEY
[Peer]
# Shield VPS 1 (primary)
PublicKey = SHIELD1_PUBLIC_KEY
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.0.0.1/32
PersistentKeepalive = 25
[Peer]
# Shield VPS 2 (secondary)
PublicKey = SHIELD2_PUBLIC_KEY
Endpoint = 203.0.113.20:51820
AllowedIPs = 10.0.1.1/32
PersistentKeepalive = 25In this setup:
- Shield VPS 1 has tunnel IP
10.0.0.1 - Shield VPS 2 has tunnel IP
10.0.1.1(different subnet to avoid routing conflicts) - The origin server connects to both shields and accepts traffic from both tunnel IPs
Update the origin firewall to allow both tunnel IPs:
sudo ufw allow from 10.0.0.1 to any port 80,443 proto tcp
sudo ufw allow from 10.0.1.1 to any port 80,443 proto tcpNote: Using separate subnets (
10.0.0.xand10.0.1.x) is recommended for multi-shield setups to avoid routing ambiguity. If both shields are in the same/24subnet, the origin might not know which shield to route return traffic through.
Adding or Removing a WireGuard Peer
Adding a New Origin Server
- Generate keys on the new origin server (Step 2 above).
- Add a new
[Peer]block to the shield VPS config. - Create the config on the new origin server.
- Restart WireGuard on the shield:
sudo systemctl restart wg-quick@wg0 - Start WireGuard on the new origin:
sudo systemctl enable --now wg-quick@wg0 - Update the domain origin address in the Lumos dashboard.
Removing an Origin Server
- Remove the
[Peer]block from the shield VPS config. - Restart WireGuard on the shield:
sudo systemctl restart wg-quick@wg0 - Stop and disable WireGuard on the origin:
sudo systemctl disable --now wg-quick@wg0 - Optionally, remove the keys:
sudo rm /etc/wireguard/private.key /etc/wireguard/public.key /etc/wireguard/wg0.conf
Security Considerations
Jurisdiction Isolation
For maximum privacy, place your shield VPS and origin server in different countries and with different providers. With WireGuard:
- The shield VPS provider sees encrypted traffic going to the origin but cannot read it.
- The origin provider does not know the shield VPS is a proxy (it only sees WireGuard tunnel traffic from a single IP).
- No single provider or jurisdiction has access to both the decrypted traffic and the origin server.
This is useful for protecting whistleblower sites, privacy-sensitive services, or applications that require strong jurisdictional separation. See VPS Providers for recommendations on providers in different jurisdictions.
Key Rotation
WireGuard keys do not expire, but it is good practice to rotate them periodically (e.g., every 6-12 months) or immediately if you suspect a key compromise:
- Generate new keys on both servers (Step 2).
- Update the configurations with new keys (Steps 3-4).
- Restart WireGuard on both servers:
# On both servers
sudo systemctl restart wg-quick@wg0- Verify the tunnel is working (Step 6).
Tip: When rotating keys, update the shield VPS config first (with the new origin public key), then update the origin config (with the new shield public key). Restart both simultaneously. The tunnel will be briefly down during the restart (typically under 2 seconds).
Private Key Security
- The private key file (
/etc/wireguard/private.key) must have permissions600(owner-only read/write). - The WireGuard config file (
/etc/wireguard/wg0.conf) also contains the private key and should have permissions600. - Never commit private keys to version control, share them in chat, or include them in scripts.
- If you suspect a private key has been compromised, rotate keys immediately on both servers.
Troubleshooting
Tunnel is up but no traffic flowing:
- Check
AllowedIPson both sides. The shield must allow the origin's tunnel IP (10.0.0.2/32) and the origin must allow the shield's tunnel IP (10.0.0.1/32). - Verify HAProxy is configured to send traffic to the tunnel IP (
10.0.0.2), not the origin's public IP. Check the domain settings in Dashboard -> Domains. - Run
sudo wg showto check handshake status. If "latest handshake" is missing, the peers have not connected. - Check if the origin server's firewall is blocking traffic on the wg0 interface. Some firewall configurations block all traffic not explicitly allowed per interface.
Handshake not completing:
- Ensure UDP port 51820 is open on the shield VPS:
# Check if the port is listening
sudo ss -ulnp | grep 51820
# Test from the origin server
nc -vzu SHIELD_PUBLIC_IP 51820- Verify the
Endpointin the origin's config points to the correct shield VPS public IP and port. - Check that public keys are correct on both sides. A common mistake is using the private key instead of the public key in the
[Peer]section, or swapping the keys between servers. - Verify the config file syntax is correct:
sudo wg-quick strip wg0should print the config without errors.
High latency through the tunnel:
- WireGuard itself adds less than 1ms of latency. High latency is due to geographic distance between servers or network congestion.
- Diagnose the path:
mtr 10.0.0.2from the shield VPS. - Consider choosing a shield VPS provider geographically closer to your origin. See VPS Providers for options.
WireGuard not starting on boot:
- Verify the service is enabled:
sudo systemctl is-enabled wg-quick@wg0 - Check for configuration errors by starting manually:
sudo wg-quick up wg0(shows errors in real time). - Verify the config file has correct permissions:
ls -la /etc/wireguard/wg0.conf(should be-rw-------). - Check the system journal for errors:
sudo journalctl -u wg-quick@wg0 --no-pager -n 50
Connection drops intermittently:
- If the origin is behind NAT, ensure
PersistentKeepalive = 25is set in the origin's[Peer]section. - Check for MTU issues. WireGuard's default MTU is 1420 (to fit inside standard 1500-byte Ethernet frames with WireGuard overhead). Some networks with additional encapsulation (e.g., PPPoE) may need a lower MTU:
# Add to the [Interface] section of wg0.conf on both servers
MTU = 1380- Check system logs for WireGuard-related errors:
dmesg | grep wireguard
After agent config push, traffic stops flowing:
- A Lumos config push triggers a HAProxy reload. This should not affect the WireGuard tunnel, as WireGuard operates at the network level independently of HAProxy.
- Verify the domain's origin address in the Lumos dashboard is still set to the tunnel IP (
10.0.0.2), not the public IP. - Check HAProxy is routing to the correct backend:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
Next Steps
- Origin Firewall -- Lock down your origin to only accept traffic from the WireGuard tunnel
- Multiple Servers -- Set up WireGuard with multiple shield servers for redundancy
- Failover -- Configure DNS failover between multiple shield servers
- Domains -- Update origin addresses to use WireGuard tunnel IPs
- SSL/TLS -- Configure SSL certificates on your shield servers
- VPS Providers -- Choose providers in different jurisdictions for maximum privacy
- Architecture -- Understand how WireGuard fits into the overall Lumos Gate architecture
- Troubleshooting -- General troubleshooting guide