From 836c084653350d55f811677216ec9bf9e98a8b4f Mon Sep 17 00:00:00 2001
From: Jack Carter <128555021+SunsetDrifter@users.noreply.github.com>
Date: Tue, 12 May 2026 17:35:19 +0200
Subject: [PATCH] docs: add Site-to-VPN scenario guide
New dedicated guide for letting clientless devices on a local network
initiate connections to NetBird peers (the reverse of VPN-to-Site).
Covers static-route and DNAT options, DNS resolution via dnsmasq, and
explains why the target peer always observes the routing peer's NetBird
IP as the source.
- Add src/pages/manage/network-routes/use-cases/by-scenario/site-to-vpn.mdx
- Wire it into NavigationDocs.jsx
- Update the site-to-site overview table row to point at the new page
- Replace the stub Site-to-VPN subsection in site-to-site-office.mdx
with a pointer to the new guide
---
src/components/NavigationDocs.jsx | 4 +
.../by-scenario/site-to-site-office.mdx | 20 +-
.../use-cases/by-scenario/site-to-vpn.mdx | 346 ++++++++++++++++++
src/pages/use-cases/site-to-site/index.mdx | 2 +-
4 files changed, 356 insertions(+), 16 deletions(-)
create mode 100644 src/pages/manage/network-routes/use-cases/by-scenario/site-to-vpn.mdx
diff --git a/src/components/NavigationDocs.jsx b/src/components/NavigationDocs.jsx
index 93f7f15e..804e4810 100644
--- a/src/components/NavigationDocs.jsx
+++ b/src/components/NavigationDocs.jsx
@@ -262,6 +262,10 @@ export const docsNavigation = [
title: 'Site-to-Site: Cloud',
href: '/manage/network-routes/use-cases/by-scenario/site-to-site-cloud',
},
+ {
+ title: 'Site-to-VPN',
+ href: '/manage/network-routes/use-cases/by-scenario/site-to-vpn',
+ },
{
title: 'Exit Nodes',
href: '/manage/network-routes/use-cases/by-scenario/exit-nodes',
diff --git a/src/pages/manage/network-routes/use-cases/by-scenario/site-to-site-office.mdx b/src/pages/manage/network-routes/use-cases/by-scenario/site-to-site-office.mdx
index 26ed7b8e..775ed26b 100644
--- a/src/pages/manage/network-routes/use-cases/by-scenario/site-to-site-office.mdx
+++ b/src/pages/manage/network-routes/use-cases/by-scenario/site-to-site-office.mdx
@@ -178,21 +178,11 @@ ping 10.0.0.100 # HQ server
## Site-to-VPN: Office Systems Reaching Remote Workers
-Some scenarios require office systems to initiate connections to remote workers (monitoring, management tools, etc.).
-
-### Configuration
-
-1. Create a network route for the office network (as above)
-2. On the office system that needs to reach remote workers, add a route to the NetBird network:
-
-```bash
-# Route to NetBird network through the routing peer
-sudo ip route add 100.64.0.0/10 via 10.0.0.50
-```
-
-Where `10.0.0.50` is the routing peer's office IP.
-
-3. Create two access policies allowing traffic in both directions
+Some scenarios require office systems without NetBird to initiate
+connections to NetBird-connected peers (monitoring, backups, management
+tools, etc.). See [Site-to-VPN](/manage/network-routes/use-cases/by-scenario/site-to-vpn)
+for the full step-by-step guide, including DNS resolution and the
+per-service port-forwarding alternative.
## Best Practices for Business Deployments
diff --git a/src/pages/manage/network-routes/use-cases/by-scenario/site-to-vpn.mdx b/src/pages/manage/network-routes/use-cases/by-scenario/site-to-vpn.mdx
new file mode 100644
index 00000000..4a1ad3db
--- /dev/null
+++ b/src/pages/manage/network-routes/use-cases/by-scenario/site-to-vpn.mdx
@@ -0,0 +1,346 @@
+import { Note, Warning } from '@/components/mdx'
+
+# Site-to-VPN: Clientless Devices Reaching NetBird Peers
+
+This guide shows how to let a device that does **not** have NetBird installed
+initiate a connection to a NetBird peer over the overlay — the reverse of the
+more common VPN-to-Site direction.
+
+## What You'll Achieve
+
+After following this guide, a clientless device on your local network can
+reach a NetBird peer by its overlay IP or NetBird DNS name. Typical examples:
+
+- An on-premise monitoring system pushing metrics to a NetBird-connected
+ collector
+- A legacy server initiating outbound backups to a NetBird peer in the cloud
+- An office printer reporting status to a NetBird-connected management
+ application
+
+```
+Clientless Device ──► Routing Peer ──► NetBird Tunnel ──► NetBird Peer
+ (no NetBird) (peer) (peer)
+```
+
+
+Site-to-VPN is implemented with [Network Routes](/manage/network-routes). The newer [Networks](/manage/networks) feature does not yet expose
+the routed CIDR as a policy source, which is required for this scenario.
+
+
+
+With Network Routes, the overlay peer will always observe the **routing
+peer's NetBird IP** as the source of the request, not the clientless
+device's local IP. See [Source IP Behavior](#source-ip-behavior) below for
+the details and why this is the case.
+
+
+## Prerequisites
+
+- A [NetBird cloud account](https://app.netbird.io/) or [self-hosted instance](/selfhosted/selfhosted-quickstart)
+- A Linux device on the local network to serve as the routing peer (a small
+ VM, NUC, or Raspberry Pi works)
+- A separate device that will run the NetBird peer the clientless device
+ needs to reach
+- The ability to either add a static route on the clientless device (or its
+ upstream router), or to install a port-forwarding rule on the routing peer
+ — both options are covered below
+
+## Example Setup
+
+- **Local site:** `192.168.50.0/24`
+- **Routing peer (`site-router`):** site IP `192.168.50.10`, NetBird IP
+ assigned at enrollment
+- **Clientless device:** `192.168.50.20`
+- **Target NetBird peer (`backup-collector`):** runs the NetBird client; we
+ reach it on TCP port `8080`
+
+## Step 1: Create Setup Keys with Groups
+
+Before installing NetBird on the routing peer, create a setup key with an
+auto-assigned group so the peer lands in the right place:
+
+1. Go to **Setup Keys** in the NetBird dashboard
+2. Click **Create Setup Key**
+3. Configure:
+ - Network Identifier: "Site Routing Peer"
+ - Auto-assigned groups: create and add `site-routing-peers`
+4. Click **Create** and note the key
+
+Repeat for the target peer if it isn't already enrolled — place it in a
+group like `backup-collectors`. The target peer is a regular NetBird peer;
+nothing special needs to be configured on it for this scenario.
+
+## Step 2: Deploy the Routing Peer
+
+Install NetBird on the routing peer and enroll it:
+
+```bash
+curl -fsSL https://pkgs.netbird.io/install.sh | sh
+sudo netbird up --setup-key YOUR_SITE_SETUP_KEY
+```
+
+Confirm the peer appears in the dashboard and shows the `site-routing-peers`
+group.
+
+## Step 3: Enable IP Forwarding on the Routing Peer
+
+The routing peer needs to forward packets between its local interface and
+the NetBird wireguard interface:
+
+```bash
+sudo sysctl -w net.ipv4.ip_forward=1
+echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-netbird-routing.conf
+```
+
+NetBird installs its own `FORWARD` chain on the routing peer that allows
+established/related return traffic. No additional iptables rules are needed
+on the routing peer itself when masquerade is enabled on the route (next
+step) — NetBird handles the source NAT.
+
+## Step 4: Create the Network Route
+
+In the NetBird dashboard:
+
+1. Go to **Network Routes** and click **Add Route**
+2. Configure:
+ - **Network Identifier:** `site-50`
+ - **Network range:** `192.168.50.0/24`
+ - **Routing Peer:** `site-router` (or use the `site-routing-peers` group)
+ - **Distribution Groups:** `backup-collectors` (the group containing the
+ peers the clientless device will reach)
+ - **Masquerade:** **Enabled** (required — see
+ [Source IP Behavior](#source-ip-behavior))
+ - **Access Control Groups:** leave empty for site-to-VPN (these only
+ apply to the VPN-to-Site direction)
+3. Click **Add Route**
+
+The route advertises the site's CIDR onto the overlay, which gives the
+target peer a return path for any traffic that originates from the site.
+
+## Step 5: Create the Access Policy
+
+The routing peer needs to be allowed to reach the target peer over the
+overlay:
+
+1. Go to **Access Control** → **Policies** and click **Add Policy**
+2. Configure:
+ - **Name:** `Site Router to Backup Collector`
+ - **Protocol:** `TCP`
+ - **Ports:** `8080` (or `All` to allow any port)
+ - **Source Groups:** `site-routing-peers`
+ - **Destination Groups:** `backup-collectors`
+3. Click **Add Policy**
+
+Because the route has masquerade enabled, traffic from the clientless
+device will appear on the overlay as originating from the routing peer's
+NetBird IP. The policy authorises that flow.
+
+## Step 6: Direct Site Traffic Through the Routing Peer
+
+You have two options for getting the clientless device's traffic to the
+routing peer. Pick whichever fits your environment.
+
+### Option A: Static Route (recommended)
+
+Tell the clientless device — or the site's upstream router — to send
+traffic destined for NetBird's CIDR (`100.64.0.0/10`) through the routing
+peer.
+
+**On a Linux clientless device:**
+
+```bash
+sudo ip route add 100.64.0.0/10 via 192.168.50.10
+# Persist via /etc/network/interfaces, netplan, or NetworkManager
+```
+
+**On Windows:**
+
+```powershell
+route -p add 100.64.0.0 mask 255.192.0.0 192.168.50.10
+```
+
+**On a site router that issues DHCP:** add a classless static route option
+(DHCP option 121) pointing `100.64.0.0/10` to the routing peer. Every
+device on the network will then learn the route automatically.
+
+The clientless device can now reach the target peer by its NetBird IP:
+
+```bash
+curl http://100.x.x.x:8080/
+```
+
+To use NetBird's DNS names instead of IPs, see
+[Resolving NetBird DNS Names](#resolving-netbird-dns-names) below.
+
+### Option B: Per-Service Port Forwarding
+
+If you cannot change routing on the site (or only want to expose specific
+services), DNAT the service on the routing peer instead:
+
+```bash
+sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 18080 \
+ -j DNAT --to-destination :8080
+```
+
+Where `eth0` is the routing peer's site-facing interface. The clientless
+device now reaches the service through the routing peer's local IP:
+
+```bash
+curl http://192.168.50.10:18080/
+```
+
+Each forwarded service needs its own DNAT rule. This is a good fit for a
+small number of well-known services and avoids any client-side changes.
+
+## Test Connectivity
+
+From the clientless device:
+
+```bash
+# Option A (static route): reach by NetBird IP
+curl -v http://:8080/
+
+# Option B (DNAT): reach via the routing peer's site IP
+curl -v http://192.168.50.10:18080/
+```
+
+Verify on the target peer that the request arrived:
+
+```bash
+# Replace with whatever the target peer is listening with
+sudo ss -tnp | grep :8080
+```
+
+The connection's remote address on the target peer will be the **routing
+peer's NetBird IP**, not the clientless device's local IP.
+
+## Resolving NetBird DNS Names
+
+By default, the clientless device has no way to resolve `*.netbird.cloud`
+hostnames — that lookup happens locally on each NetBird peer. You can
+publish those names to the site by running a forwarding resolver on the
+routing peer.
+
+A minimal `dnsmasq` configuration on the routing peer:
+
+```ini
+# /etc/dnsmasq.d/netbird.conf
+bind-interfaces
+listen-address=192.168.50.10
+interface=eth0
+
+# Forward NetBird-managed names to this peer's local NetBird resolver.
+# The NetBird daemon listens on the peer's NetBird IP, port 53.
+server=/netbird.cloud/
+
+# Everything else to upstream
+server=8.8.8.8
+server=1.1.1.1
+```
+
+Then point the clientless device's DNS at `192.168.50.10` (via DHCP,
+`/etc/resolv.conf`, or static configuration) and use the NetBird hostname
+directly:
+
+```bash
+curl http://backup-collector.netbird.cloud:8080/
+```
+
+
+The NetBird daemon binds its DNS resolver on the peer's own NetBird IP,
+not on `127.0.0.1`. Substitute the routing peer's actual NetBird IP into
+the `server=/netbird.cloud/...` line — you can find it with
+`netbird status` on the routing peer.
+
+
+## Source IP Behavior
+
+With Network Routes, the target peer always sees the **routing peer's
+NetBird IP** as the source — regardless of which clientless device on the
+site initiated the connection. Two routes lead to this:
+
+- With **masquerade enabled** on the route, NetBird installs a MASQUERADE
+ rule on the routing peer's outbound wireguard interface, rewriting the
+ source IP at the routing peer.
+- With **port forwarding (Option B)**, the routing peer SNATs the DNAT'd
+ flow so that return traffic comes back to the routing peer.
+
+You **cannot** preserve the clientless device's local IP all the way to
+the target peer through Network Routes. The target peer's per-peer access
+control list matches sources by NetBird IP, and there is no field on a
+legacy Network Route that registers the site CIDR as a policy source.
+Disabling masquerade does not help here: the target peer will receive
+packets with the site source IP, but they will be dropped by its access
+control because that IP is not in any allowed source group.
+
+If preserving the original source IP is a hard requirement, you have two
+choices:
+
+1. **Run the access policy with the destination group empty of per-peer
+ ACL** — practically, this means structuring policies so that no policy
+ has the target peer as a destination, which removes the per-peer ACL on
+ it entirely. This trades security for visibility and is rarely
+ appropriate.
+2. **Use the [Networks](/manage/networks) feature instead** for any
+ destinations where preserved source IP matters. Networks' Resources
+ model is designed around CIDR-bearing groups and can carry the original
+ source through to the destination's ACL. The Site-to-VPN direction is
+ still limited there at the time of writing — track the
+ [Networks documentation](/manage/networks) for updates.
+
+## Troubleshooting
+
+**Connection times out from the clientless device.**
+
+Check that the static route or DNAT rule is in place:
+
+```bash
+# On the clientless device, Option A:
+ip route get
+# Should show "via 192.168.50.10 dev "
+
+# On the routing peer, Option B:
+sudo iptables -t nat -L PREROUTING -n -v
+# Should show your DNAT rule with non-zero packet counters when traffic flows
+```
+
+**Routing peer receives packets but they don't reach the target peer.**
+
+Check that IP forwarding is enabled and confirm the target peer is reachable
+from the routing peer directly:
+
+```bash
+# On the routing peer
+cat /proc/sys/net/ipv4/ip_forward # should be 1
+curl http://:8080/ # should succeed
+```
+
+If the second command fails, the access policy is wrong — verify the policy
+allows `site-routing-peers` → target peer's group on the required port.
+
+**Target peer receives packets but drops them.**
+
+If you disabled masquerade on the route, the target peer will see the raw
+site source IP and drop the packets at its per-peer access control. Enable
+masquerade on the route to fix this.
+
+```bash
+# On the target peer, watch for drops:
+sudo iptables -L INPUT -n -v | grep DROP
+# DROP counter on the wt0 interface increasing means the packet
+# was rejected by access control.
+```
+
+**DNS resolution returns NXDOMAIN.**
+
+Confirm the routing peer's NetBird IP is correct in `dnsmasq.conf` (it
+changes if the peer is re-enrolled), and that `dnsmasq` is not bound to
+`lo` — binding loopback causes it to refuse forwarding to its own
+`127.0.0.1`-co-located NetBird resolver.
+
+## Related
+
+- [Network Routes — Concept](/manage/network-routes)
+- [Site-to-Site: Office Networks](/manage/network-routes/use-cases/by-scenario/site-to-site-office)
+- [Configuring Routes with Access Control](/manage/network-routes/use-cases/by-configuration/access-control)
+- [Site-to-Site Overview](/use-cases/site-to-site)
diff --git a/src/pages/use-cases/site-to-site/index.mdx b/src/pages/use-cases/site-to-site/index.mdx
index e8f1adc6..0e27e2b9 100644
--- a/src/pages/use-cases/site-to-site/index.mdx
+++ b/src/pages/use-cases/site-to-site/index.mdx
@@ -82,7 +82,7 @@ Your Laptop ──────► NetBird Tunnel ──────► Exit Node
|-------------|----------|----------------|
| Access home devices from my laptop | VPN-to-Site | [Networks](/manage/networks/use-cases/by-scenario/access-home-devices) |
| Access office resources while traveling | VPN-to-Site | [Networks](/manage/networks/use-cases/by-scenario/remote-worker-access) |
-| Let an office server connect to my laptop | Site-to-VPN | [Network Routes](/manage/network-routes/use-cases/by-scenario/site-to-site-office) only |
+| Let an office server connect to my laptop | Site-to-VPN | [Network Routes](/manage/network-routes/use-cases/by-scenario/site-to-vpn) only |
| Connect two home networks together | Site-to-Site | [Network Routes](/manage/network-routes/use-cases/by-scenario/site-to-site-home) only |
| Link branch offices | Site-to-Site | [Network Routes](/manage/network-routes/use-cases/by-scenario/site-to-site-office) only |
| Bridge cloud VPC with on-premise network | Site-to-Site | [Network Routes](/manage/network-routes/use-cases/by-scenario/site-to-site-cloud) only |