[UPDATE] Setting up a Split-Tunnel WireGuard VPN from HestiaCP-Managed Ubuntu to Your Home Lab

Guide: WireGuard Site-to-Site VPN with Split Routing (LAN Traffic via VPN, Internet Traffic Direct)

Introduction

When setting up a site-to-site VPN with WireGuard, a common mistake is routing all traffic from one site through the tunnel.
This can break services such as Let’s Encrypt ACME challenges, because external validation servers can no longer reach the public IP of the external machine.

The solution is to configure split routing:

  • Only traffic destined for the internal LAN subnet is routed through the VPN.
  • All other traffic remains on the external server’s normal internet connection.

This guide describes how to set up such a configuration using WireGuard on two Linux servers with UFW as firewall and NAT, and a FritzBox router at the home site.


Scenario

  • Server A: Home server behind a NAT router (FritzBox). WireGuard listens here.
  • Server B: External server with a public IP. Connects to the home LAN via VPN.
  • Home LAN: 192.168.178.0/24 (example subnet).
  • WireGuard VPN subnet: 10.8.0.0/24.
  • WireGuard port: 57089/UDP forwarded from FritzBox → Server A.
  • NAT and UFW are used on Server A to allow VPN clients to reach the LAN.

1. Configure FritzBox Port Forwarding

On the FritzBox:

  1. Go to Internet → Freigaben → Portfreigaben.

  2. Select the device (Server A) from the dropdown menu.

  3. Click New Freigabe (New rule).

  4. Choose Portfreigabe (Port forwarding).

  5. Fill in the fields:

    • Application: Custom
    • Description: wireguard
    • Protocol: UDP
    • Port an Gerät (Internal port): 57089
    • bis Port (to port): 57089
    • Port extern gewünscht (External port): 57089
  6. Check Freigabe aktivieren (Enable rule).

  7. Save with OK.

This forwards incoming UDP port 57089 from the internet to your home server.


2. Install WireGuard

On both servers:

sudo apt update
sudo apt install -y wireguard

3. Generate Keys

On each server:

wg genkey | tee private.key | wg pubkey > public.key

On one server, also generate a preshared key:

wg genpsk > preshared.key

4. Configure WireGuard

Server A (behind NAT)

/etc/wireguard/wg0.conf:

[Interface]
Address = 10.8.0.1/24
ListenPort = 57089
PrivateKey = <PRIVATE_KEY_SERVER_A>

# NAT for LAN access
PostUp = ufw route allow in on wg0 out on eth0
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

[Peer]
PublicKey = <PUBLIC_KEY_SERVER_B>
PresharedKey = <PRESHARED_KEY>
AllowedIPs = 10.8.0.2/32

Server B (external)

/etc/wireguard/wg0.conf:

[Interface]
Address = 10.8.0.2/24
PrivateKey = <PRIVATE_KEY_SERVER_B>

[Peer]
PublicKey = <PUBLIC_KEY_SERVER_A>
PresharedKey = <PRESHARED_KEY>
Endpoint = your-dyndns-domain.example.com:57089
AllowedIPs = 192.168.178.0/24, 10.8.0.1/32
PersistentKeepalive = 25

5. Enable IPv4 Forwarding on Server A

echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

6. Configure UFW and NAT (Server A)

  1. Allow WireGuard port:
sudo ufw allow 57089/udp
  1. Edit /etc/ufw/before.rules and add before the *filter section:
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
COMMIT
  1. Reload UFW:
sudo ufw reload

7. Disable IPv6 on Server B

Many routers do not forward IPv6 correctly. If the client prefers AAAA DNS, the tunnel handshake may fail. Disable IPv6 on the external server:

sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1

echo "net.ipv6.conf.all.disable_ipv6=1" | sudo tee /etc/sysctl.d/99-disable-ipv6.conf
echo "net.ipv6.conf.default.disable_ipv6=1" | sudo tee -a /etc/sysctl.d/99-disable-ipv6.conf
sudo sysctl -p /etc/sysctl.d/99-disable-ipv6.conf

8. Start WireGuard

On both servers:

sudo systemctl enable wg-quick@wg0
sudo systemctl restart wg-quick@wg0

Check status:

sudo wg show

Look for latest handshake and RX/TX traffic.


9. Verification Tests

On Server B (external):

# Ping VPN peer
ping -c3 10.8.0.1

# Ping LAN gateway and DNS
ping -c3 192.168.178.1
ping -c3 192.168.178.109

# DNS query via internal resolver
dig @192.168.178.109 fritz.box +short

# Public IP should remain external server’s
curl -4 ifconfig.me

# Routing table should show LAN via wg0, everything else via eth0
ip route show

Expected results:

  • Internal LAN IPs are reachable via VPN.
  • Public IP remains that of the external server.
  • Only 192.168.178.0/24 is routed through the tunnel.

10. Why This Works

  • Split routing is controlled by AllowedIPs:

    • 192.168.178.0/24 → through WireGuard.
    • Everything else → external server’s default route.
  • NAT on Server A ensures LAN devices reply to VPN traffic without requiring routes on the router.

  • IPv6 disabled prevents accidental AAAA resolution and connection failures.

  • FritzBox port forwarding makes sure WireGuard packets from the internet actually reach Server A.


Conclusion

This configuration builds a site-to-site VPN with WireGuard where:

  • Only internal LAN traffic is routed through the VPN.
  • Internet traffic continues directly from the external server.

This setup avoids issues such as Let’s Encrypt ACME challenges failing, which can happen if all traffic is forced through the tunnel.


Why Split-Tunnel Site-to-Site VPN is Useful

There are many practical reasons why you would want to build a site-to-site VPN where only LAN traffic goes through the tunnel while external traffic remains external. Here are some examples:


1. Self-Hosted Applications Without Exposing Them to the Internet

Imagine you run Immich (self-hosted photo management) on your home server.

  • Normally, to share albums with friends, you would have to expose Immich directly to the internet – which increases attack surface and security risks.
  • With the VPN in place, your external server acts as a reverse proxy. Friends connect to the external server, which forwards the requests securely through the VPN into your LAN where Immich runs.
  • Result: your Immich server remains completely private, never exposed directly to the internet, but is still accessible to trusted users.

2. Centralized DNS and Ad Blocking

If you run an internal DNS resolver with ad blocking (e.g. AdGuard Home or Pi-hole) in your LAN:

  • Without VPN, your external server would use public DNS (Google, Cloudflare, etc.).
  • With VPN, all DNS queries can be routed into your home network, giving you centralized DNS filtering for all your servers, whether inside or outside your LAN.

3. Secure Access to Home Devices

Many devices should never be directly exposed to the internet:

  • Printers, NAS systems, IoT devices, etc.
  • With the VPN, your external server can securely access these devices as if it were inside your LAN, but without opening risky ports on your router.

4. Protecting ACME Challenges (Let’s Encrypt Certificates)

As explained in the introduction:

  • If all traffic from the external server is routed through the VPN, Let’s Encrypt cannot validate domain ownership (because the validation server expects to see the external server’s public IP).
  • With split routing, ACME validation works correctly, while the external server can still reach the LAN when needed.

5. Scalable Multi-Site Networking

If you operate multiple locations (e.g. home, office, cloud server):

  • You can link them with site-to-site WireGuard tunnels.
  • Each site keeps its own internet breakout, but they share LAN traffic securely through VPN tunnels.
  • This is similar to enterprise VPN design, but lightweight and efficient with WireGuard.

:white_check_mark: In summary:
Split-tunnel VPNs are ideal when you want secure access to internal services (like Immich, DNS, or file shares) without breaking public-facing services (like web servers with Let’s Encrypt).

I’ve updated the original post with a complete step-by-step guide.
If you spot any mistakes or see room for improvement, I’d be very happy if you share your feedback. :slight_smile:

1 Like