Skip to content

docs: masquerade-off HA is achievable, not impossible - #893

Merged
SunsetDrifter merged 4 commits into
mainfrom
cc/masqoff-ha-routing-peers
Aug 3, 2026
Merged

docs: masquerade-off HA is achievable, not impossible#893
SunsetDrifter merged 4 commits into
mainfrom
cc/masqoff-ha-routing-peers

Conversation

@SunsetDrifter

@SunsetDrifter SunsetDrifter commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Why

Two pages state that high availability "stops working" when masquerade is off:

  • manage/networks/how-routing-peers-work — "High availability also stops working with masquerade off, because return traffic must flow back through one specific routing peer's LAN address"
  • manage/networks/masquerade — "High availability stops working"

That reads as a flat impossibility, and it turns away a legitimate use case: preserving the client source IP for auditing, per-user database permissions, or firewall policy keyed on the real source.

The true statement is narrower. NetBird's half of a failover still works, clients move to the standby peer on their own. What is missing is the return route inside the destination network following that move. That is a condition to meet, not a dead end.

What changed

masquerade.mdx

  • The "stops working" bullet now points to a new short section.
  • New High availability with masquerade off section: give the routing peers distinct metrics so the active peer is the same for every client, then make the return route's next hop follow that peer, either with a shared virtual IP (VRRP) or a router that moves the route on a failed health check. The health check has to track the tunnel rather than the host answering pings.
  • A warning against pointing the return route at both peers, as two static routes or an equal-cost pair. It is the intuitive fix and it fails silently: replies arriving at the peer a client is not using are discarded, so both ends look healthy.
  • A note that the second interruption on recovery cannot be avoided by pinning the virtual IP to the standby, because the client's switch back to the lower-metric peer is automatic. Pinning the route while the client moves back strands the return path rather than protecting it.
  • The return route may live on the destination subnet's default gateway even when that gateway and the routing peer share the subnet. This is the only option when the destination host is not the operator's to configure, which is common for appliances and managed databases. The page previously offered the gateway only for a destination in a different subnet, and its persistent-configuration steps assume host access throughout.

Both mechanisms live outside NetBird, so the section names the pattern without shipping a third-party configuration we would then own.

how-routing-peers-work.mdx

  • The masquerade note now states the condition instead of the impossibility, and links to the new section.
  • "Failover is automatic and immediate" softened to "normally within seconds". Client re-selection is fast but not instant, and "immediate" invites a report from anyone who measures it. Also rephrased so it no longer asserts whether the client or the management side detects the loss.

Notes for review

  • No new pages, no navigation changes. Anchors used: #high-availability-with-masquerade-off (new) and #high-availability (already referenced from the Kubernetes use-case page).
  • The timing wording is deliberately qualitative. Publishing a figure would read as a commitment.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Summary by CodeRabbit

  • Documentation
    • Clarified automatic failover timing and switch-back behavior for routing peers.
    • Expanded masquerade-off guidance, including return-route requirements and failover behavior.
    • Specified that the NetBird CIDR return route must target the routing peer’s LAN IP and be installed on the destination host or subnet gateway.
    • Documented Linux-only support for disabling masquerade, operational responsibilities for high availability, supported failover approaches, and expected brief interruptions.

Both the routing-peers page and the masquerade page stated that high
availability "stops working" with masquerade off. That reads as a flat
impossibility and loses a legitimate use case (source IP preservation for
audit, per-user database rules, or firewall policy keyed on the real source).

What is actually true is narrower: NetBird's half of the failover still
works, clients move to the standby peer on their own. What is missing is the
return route inside the destination network following that move. State the
condition instead of the impossibility, and add a short section on what
meeting it takes: distinct metrics so the active peer is the same for every
client, plus a virtual IP or a router-moved route so the next hop follows.
Both live outside NetBird, so the section names the pattern rather than
shipping a third-party config we would then own.

Also:

- Soften "failover is automatic and immediate". Clients re-select within
  seconds, which is fast but not instant, and "immediate" invites a support
  ticket from anyone who measures it. Phrased so it does not assert whether
  the client or the management side detects the loss, which this change did
  not verify.
- Warn against pointing the return route at both peers, as two static routes
  or an equal-cost pair. It is the intuitive fix and it fails silently.
- Note that the second interruption on recovery cannot be avoided by pinning
  the virtual IP to the standby, since the client's switch back is automatic.
  Pinning the route while the client moves back strands the return path.
- The return route can live on the destination subnet's default gateway even
  when the gateway and the routing peer share that subnet, which is the only
  option when the destination host is not the customer's to configure. The
  page previously offered the gateway only for a different-subnet
  destination, and its persistent-config steps assume host access throughout.
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Jul 30, 2026 8:27am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 813134c7-1f90-44c8-a3e3-317ecb77c38e

📥 Commits

Reviewing files that changed from the base of the PR and between 51fb1ad and b2ca60d.

📒 Files selected for processing (3)
  • src/pages/manage/networks/how-routing-peers-work.mdx
  • src/pages/manage/networks/index.mdx
  • src/pages/manage/networks/masquerade.mdx

📝 Walkthrough

Walkthrough

The documentation updates clarify automatic routing peer failover timing, masquerade-off return-route placement, and high-availability configurations that keep return traffic aligned with the active peer.

Changes

Routing and masquerade documentation

Layer / File(s) Summary
Failover timing and failure behavior
src/pages/manage/networks/how-routing-peers-work.mdx, src/pages/manage/networks/index.mdx
Documents automatic standby takeover timing, stale return routes that can drop replies, and separate high-availability responsibility when masquerade is disabled.
Masquerade-off return routing and HA setup
src/pages/manage/networks/masquerade.mdx
Clarifies return-route placement and adds requirements for metrics, next-hop tracking, route convergence, and failover interruptions.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: techhuttv

Poem

I thump my feet where routes align,
Through failover hops and paths that shine.
The standby wakes when primaries fall,
Return roads guide the packets all.
A bunny keeps the paths precise.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main documentation change: clarifying that high availability with masquerade disabled is possible.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cc/masqoff-ha-routing-peers

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/pages/manage/networks/how-routing-peers-work.mdx

Oops! Something went wrong! :(

ESLint: 9.39.2

TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'configs' -> object with constructor 'Object'
| property 'flat' -> object with constructor 'Object'
| ...
| property 'plugins' -> object with constructor 'Object'
--- property 'react' closes the circle
Referenced from:
at JSON.stringify ()
at file:///node_modules/@eslint/eslintrc/lib/shared/config-validator.js:308:45
at Array.map ()
at ConfigValidator.formatErrors (file:///node_modules/@eslint/eslintrc/lib/shared/config-validator.js:299:23)
at ConfigValidator.validateConfigSchema (file:///node_modules/@eslint/eslintrc/lib/shared/config-validator.js:330:84)
at ConfigArrayFactory._normalizeConfigData (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:676:19)
at ConfigArrayFactory._loadConfigData (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:641:21)
at ConfigArrayFactory._loadExtendedShareableConfig (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:946:21)
at ConfigArrayFactory._loadExtends (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:814:25)
at ConfigArrayFactory._normalizeObjectConfigDataBody (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:752:25)

src/pages/manage/networks/index.mdx

Oops! Something went wrong! :(

ESLint: 9.39.2

TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'configs' -> object with constructor 'Object'
| property 'flat' -> object with constructor 'Object'
| ...
| property 'plugins' -> object with constructor 'Object'
--- property 'react' closes the circle
Referenced from:
at JSON.stringify ()
at file:///node_modules/@eslint/eslintrc/lib/shared/config-validator.js:308:45
at Array.map ()
at ConfigValidator.formatErrors (file:///node_modules/@eslint/eslintrc/lib/shared/config-validator.js:299:23)
at ConfigValidator.validateConfigSchema (file:///node_modules/@eslint/eslintrc/lib/shared/config-validator.js:330:84)
at ConfigArrayFactory._normalizeConfigData (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:676:19)
at ConfigArrayFactory._loadConfigData (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:641:21)
at ConfigArrayFactory._loadExtendedShareableConfig (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:946:21)
at ConfigArrayFactory._loadExtends (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:814:25)
at ConfigArrayFactory._normalizeObjectConfigDataBody (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:752:25)

src/pages/manage/networks/masquerade.mdx

Oops! Something went wrong! :(

ESLint: 9.39.2

TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'configs' -> object with constructor 'Object'
| property 'flat' -> object with constructor 'Object'
| ...
| property 'plugins' -> object with constructor 'Object'
--- property 'react' closes the circle
Referenced from:
at JSON.stringify ()
at file:///node_modules/@eslint/eslintrc/lib/shared/config-validator.js:308:45
at Array.map ()
at ConfigValidator.formatErrors (file:///node_modules/@eslint/eslintrc/lib/shared/config-validator.js:299:23)
at ConfigValidator.validateConfigSchema (file:///node_modules/@eslint/eslintrc/lib/shared/config-validator.js:330:84)
at ConfigArrayFactory._normalizeConfigData (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:676:19)
at ConfigArrayFactory._loadConfigData (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:641:21)
at ConfigArrayFactory._loadExtendedShareableConfig (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:946:21)
at ConfigArrayFactory._loadExtends (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:814:25)
at ConfigArrayFactory._normalizeObjectConfigDataBody (file:///node_modules/@eslint/eslintrc/lib/config-array-factory.js:752:25)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/manage/networks/masquerade.mdx`:
- Line 184: Update the recovery interruption statement in the masquerade
failover documentation to make it conditional: recovery can cause a second brief
interruption when clients return before the route does, and the behavior depends
on convergence timing. Preserve the surrounding explanation about automatic
client switching and virtual IP pinning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: da772466-f08a-4d07-8b2a-ce88319d735a

📥 Commits

Reviewing files that changed from the base of the PR and between 0977b7e and 6690f06.

📒 Files selected for processing (2)
  • src/pages/manage/networks/how-routing-peers-work.mdx
  • src/pages/manage/networks/masquerade.mdx

Comment thread src/pages/manage/networks/masquerade.mdx Outdated
A routing peer can stay enrolled and report healthy, with its WireGuard
interface present, while being unable to forward from the tunnel to the
destination subnet: a failed LAN interface, or a firewall change. Nothing
fails over in that state. NetBird sees a reachable peer, and a health check
that only looks for the interface sees a healthy one, so the return route
stays pointed at a peer that forwards nothing until someone intervenes.

Say that the check has to reach a destination through the tunnel.
Measured on a routing-peer pair with masquerade off: a held TCP connection
survives the switch. Four failovers, graceful shutdown and hard kill, all
showed the same signature — one round trip with multi-second latency, then the
same connection continuing with monotonic sequence numbers. No resets, and no
RST on the wire in any of them.

The reason is a property of masquerade being off: the addresses on the
connection do not change when another peer takes over, so the flow is portable
between peers, and the routed ACL accepts on addresses rather than on
connection state, so the standby forwards mid-stream packets it never saw a
handshake for.

Note this is specific to masquerade being off. The general high-availability
section still says established connections reset, which is the masquerade-on
default, where the standby would translate to a different source address.

Also trims the mechanism guidance to name the pattern rather than lean on any
one implementation, since which mechanism is right may change.
Review of the branch turned up three problems.

The Networks overview still said disabling masquerade 'breaks high
availability', one click from the page arguing it does not. Reworded to say it
makes high availability something you arrange rather than something you get.

Neither reset/stall sentence named its masquerade scope, so a reader with
masquerade off met both and they flatly disagreed. The general failover section
now scopes its reset claim to masquerade on, names the reason (the standby
translates to a different source address), and links onward.

'Stalls for the length of the failover' understated it. Measured stalls were 6
to 13 seconds against a 6 second failover, because the sender waits for its
next retransmit after the path returns. Says so, with the range, and keeps the
claim hedged to what was observed rather than asserting connections are never
broken.
@SunsetDrifter
SunsetDrifter marked this pull request as ready for review August 3, 2026 07:49
@SunsetDrifter
SunsetDrifter merged commit 3f02e40 into main Aug 3, 2026
4 of 5 checks passed
@SunsetDrifter
SunsetDrifter deleted the cc/masqoff-ha-routing-peers branch August 3, 2026 07:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants