Covers 0.0.0.0/0 route creation, opt-in client-side selection (different
from subnet routes which auto-apply), and the extra trust exposure when
routing through a third-party VPS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Why: CT124 observed "Connecting" from iPhone for hours while
CT124 self-reported Connected. Root cause: DNS timeout ~7h earlier
broke management gRPC, daemon didn't auto-recover after DNS returned.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
VirMach-assigned OS hostname wasn't very identifiable. Renamed the
NetBird peer (and NB_HOSTNAME env) to virmach-lax — vendor + airport
code convention that scales if more VirMach VPSes in other regions
get added later (virmach-nrt, virmach-fra, etc).
New FQDN: virmach-lax.netbird.selfhosted
Operations reference: ssh root@virmach-lax.netbird.selfhosted
OS-level hostname left as StrongHotpink-VM (provider default).
External VM 141.11.93.252 had SSH locked to specific source IPs
(tailscale0, HiNet WAN, one other). Mac couldn't reach it from
other ISPs. Solved by joining the VM to our NetBird mesh and adding
an iptables ACCEPT rule for wt0:22.
Run into the same Tailscale kernel-mode iptables-legacy collision
already documented in peer-deployment-ops.md — systemd oneshot
workaround fixed it.
Doc now describes both the temporary ProxyJump solution and the
permanent mesh-membership solution, plus a config strategy of
retiring per-IP whitelists in favor of a single wt0 ACCEPT rule.
Dashboard's 'SSH Access is disabled' warning confuses new users
who can clearly ssh into peers just fine. These are two different
systems: Dashboard's SSH Access is NetBird's own in-daemon SSH
tunnel server (accessed via Dashboard Connect button or
'netbird ssh'), while the regular ssh command uses the target
machine's OpenSSH running on port 22 and has nothing to do with
the Dashboard SSH toggle.
Home-lab use cases rarely need the NetBird built-in SSH — regular
OpenSSH over the NetBird overlay is sufficient. Doc explains when
you would actually want it (browser-based terminal from Dashboard,
credential offloading to NetBird identity, etc) and how to enable
if needed.
Document two lessons from the RTSP-camera-via-subnet-route diagnosis:
1. Existing NetBird clients don't automatically re-apply new route
config; daemons stuck in old state need explicit restart per
platform (launchctl kickstart / App reconnect / compose restart).
Shows up as 'Networks: -' on status output and no entry in the
system routing table.
2. Some iOS apps (notably certain RTSP players) don't use the system
VPN route and bypass NetBird entirely even when route is
correctly installed. Safari test distinguishes 'route broken'
from 'app doesn't respect VPN'.
CT100 now routes 192.168.42.0/24 for the personal group, letting
Mac/iPhone reach LAN devices that don't run NetBird (OpenWrt,
Proxmox nodes, NAS, printers, cameras, etc).
- IP forwarding persisted via /etc/sysctl.d/99-netbird-route.conf
- Route created with masquerade=true, metric=9999
- access_control_groups restricts who can use the route
- Chose CT100 over CT124 to avoid the Tailscale kernel-mode
iptables collision documented in peer-deployment-ops.md
Doc covers: concept, API + UI creation, verification, troubleshooting
(forwarding, masquerade, LXC caveats, Tailscale interference),
and security considerations for narrowing the route CIDR.
Discovered while deploying NetBird on CT124 (which already runs
Tailscale in kernel mode). Tailscale's ts-input/ts-forward chains
drop all 100.64.0.0/10 traffic not coming from tailscale0, and since
NetBird uses 100.71.0.0/16 (within that CGNAT range), its wt0
traffic gets silently dropped.
Captured:
- Symptom recognition (Connected in status but 100% ping loss)
- Distinguishing when it hits (kernel-mode Tailscale vs userspace)
- Fix: iptables-legacy -I INPUT/FORWARD allow rules on wt0
- Persistence: systemd oneshot + /usr/local/sbin script
(iptables-persistent doesn't cover the legacy table)
Applied the segmentation plan. Added an 'execution log' section with
real IDs, verification results, and a rollback snippet.
Key lesson corrected in the main plan: the original design missed
personal<->personal connectivity (Mac<->iPhone would have broken
when Default was disabled). Added personal-internal policy and a
note that every desired group pair — including a group with itself
— needs an explicit policy, because NetBird defaults to deny when
there is no matching policy.
Explain what Groups are in NetBird, the four places they surface
(policies, setup keys auto_groups, network routes, DNS), and a
concrete segmentation plan for the current mesh:
- servers group: CT100, CT101
- personal group: mbp-13-pro, iphone-15-pro
- Replace default All->All with least-privilege policies
(personal->servers, servers<->servers)
Includes UI and API workflows, a phased migration plan, rollback
strategy, and how to wire auto_groups into setup keys for future
deployments.
Document the operational patterns established while adding CT100 and
CT101 as peers via Docker, plus incidental fixes:
- Docker compose template for headless peer containers
- One-off setup key naming convention and API creation
- Peer rename via API PUT (works, unlike setup keys)
- Mac daemon kickstart via 'sudo launchctl kickstart -k system/netbird'
(service name is 'netbird', not 'io.netbird.client')
- UPNP auto-port-mapping discovery in NetBird client
- CT100 PermitRootLogin drop-in override (first-match-wins sshd quirk)
Document the 'Force relay connection' switch that silently disables
ICE gathering on iOS NetBird (and similar on macOS), which was the
real cause of all peers showing Connection type: Relayed despite
STUN being correctly configured.
Also document bufferbloat diagnosis via macOS networkQuality, which
was a red herring during the P2P investigation (large RTT spikes
were the WiFi/ISP, not NetBird).
Document Setup Keys concept vs SSO, PAT creation and usage, and the
workaround for renaming a setup key — the API silently ignores the
name field on PUT, so the fix is to UPDATE store.db directly and
restart netbird-server.
Document the fix for 'server closed the stream without sending trailers'
gRPC error when exposing NetBird via Nginx Proxy Manager:
- NPM proxy_host advanced_config with grpc_pass for Signal/Management
- Caddy h2c listener enabled via 'protocols h1 h2 h2c'