[GH-ISSUE #6248] Router peer enters infinite ipset-creation loop (100% CPU, mesh dead) when routing a Network with a large domain-resource set #12936

Closed
opened 2026-08-05 02:07:02 -04:00 by saavagebueno · 2 comments
Owner

Originally created by @ivanmankov on GitHub (May 22, 2026).
Original GitHub issue: https://github.com/netbirdio/netbird/issues/6248

Describe the bug

On a Linux router peer that serves a Network containing a large domain-resource set (~2500 resources), the client daemon enters an infinite loop creating the same nftables ipsets over and over and never converges. One goroutine pegs a CPU core at 100%+, the daemon stops responding on its UDS socket (netbird status hangs), the WireGuard handshake never completes, and the peer becomes unreachable over the mesh.

The hot path is client/firewall/nftables/router_linux.go — the log is flooded (~6 lines/sec, each set logged twice, cycling through set IDs, all with 0 prefixes):

INFO client/firewall/nftables/router_linux.go:584: Created new ipset: nb-7e5a16be with 0 prefixes
INFO client/firewall/nftables/router_linux.go:584: Created new ipset: nb-7e5a16be with 0 prefixes
INFO client/firewall/nftables/router_linux.go:584: Created new ipset: nb-9f9061a1 with 0 prefixes
...

Trigger

The loop starts on a cold start of the router daemon (restart or package upgrade) while it is a router for a Network with a large domain-resource list. A daemon that was already running stays fine until it is restarted.

Environment

  • NetBird client (self-hosted): reproduced on 0.71.2 and 0.71.3; also observed on 0.71.0 and after downgrade to 0.70.5 — so the bug predates the 0.71 line.
  • OS: Linux (Ubuntu), kernel WireGuard (wt0), single/dual vCPU exit nodes.
  • Peer is a router (masquerade exit) in a Network with ~2500 domain resources (wildcard + apex domains).

Steps to reproduce

  1. Create a Network with a large domain-resource set (~2500 resources).
  2. Assign a Linux peer as its router (masquerade enabled).
  3. Restart the netbird client daemon on that peer (or upgrade the package).
  4. Observe client.log flooding with Created new ipset … with 0 prefixes, one core at 100%+, daemon unresponsive, mesh IP unreachable.

Expected behavior

Router programs each ipset once, converges, and serves traffic.

Actual behavior

Endless re-creation of the same ipsets; daemon never reaches steady state; mesh dies on that peer.

Secondary manifestation

Disabling the heavy router via the Management API while the daemon is already running (so it must tear down the large set incrementally, without a restart) leaves it in a quieter variant of the same problem: a single goroutine spins a core at ~80% without log spam, throttling forwarded throughput. A clean restart after the set is reduced converges normally.

Impact

Any restart/upgrade of an exit/router node serving a large domain-resource Network takes that node offline until the set is reduced. On HA setups it cascades to the fallback exit if both are restarted.

Workaround

Keep the domain-resource set on any single router small (a few hundred). Reducing the set to ~360 resources let cold start converge normally on the same hardware/version.

Originally created by @ivanmankov on GitHub (May 22, 2026). Original GitHub issue: https://github.com/netbirdio/netbird/issues/6248 ### Describe the bug On a Linux router peer that serves a Network containing a **large domain-resource set (~2500 resources)**, the client daemon enters an **infinite loop** creating the same nftables ipsets over and over and never converges. One goroutine pegs a CPU core at 100%+, the daemon stops responding on its UDS socket (`netbird status` hangs), the WireGuard handshake never completes, and the peer becomes **unreachable over the mesh**. The hot path is `client/firewall/nftables/router_linux.go` — the log is flooded (~6 lines/sec, each set logged twice, cycling through set IDs, all `with 0 prefixes`): ``` INFO client/firewall/nftables/router_linux.go:584: Created new ipset: nb-7e5a16be with 0 prefixes INFO client/firewall/nftables/router_linux.go:584: Created new ipset: nb-7e5a16be with 0 prefixes INFO client/firewall/nftables/router_linux.go:584: Created new ipset: nb-9f9061a1 with 0 prefixes ... ``` ### Trigger The loop starts on a **cold start of the router daemon** (restart or package upgrade) while it is a router for a Network with a large domain-resource list. A daemon that was already running stays fine until it is restarted. ### Environment - NetBird client (self-hosted): reproduced on **0.71.2 and 0.71.3**; also observed on **0.71.0** and after downgrade to **0.70.5** — so the bug predates the 0.71 line. - OS: Linux (Ubuntu), kernel WireGuard (`wt0`), single/dual vCPU exit nodes. - Peer is a router (masquerade exit) in a Network with ~2500 domain resources (wildcard + apex domains). ### Steps to reproduce 1. Create a Network with a large domain-resource set (~2500 resources). 2. Assign a Linux peer as its router (masquerade enabled). 3. Restart the `netbird` client daemon on that peer (or upgrade the package). 4. Observe `client.log` flooding with `Created new ipset … with 0 prefixes`, one core at 100%+, daemon unresponsive, mesh IP unreachable. ### Expected behavior Router programs each ipset once, converges, and serves traffic. ### Actual behavior Endless re-creation of the same ipsets; daemon never reaches steady state; mesh dies on that peer. ### Secondary manifestation Disabling the heavy router via the Management API while the daemon is **already running** (so it must tear down the large set incrementally, without a restart) leaves it in a quieter variant of the same problem: a single goroutine spins a core at ~80% **without log spam**, throttling forwarded throughput. A clean restart **after** the set is reduced converges normally. ### Impact Any restart/upgrade of an exit/router node serving a large domain-resource Network takes that node offline until the set is reduced. On HA setups it cascades to the fallback exit if both are restarted. ### Workaround Keep the domain-resource set on any single router small (a few hundred). Reducing the set to ~360 resources let cold start converge normally on the same hardware/version.
Author
Owner

@linear-code[bot] commented on GitHub (May 22, 2026):

NET-1200

<!-- gh-comment-id:4519075774 --> @linear-code[bot] commented on GitHub (May 22, 2026): <!-- linear-linkback --> <p><a href="https://linear.app/netbird/issue/NET-1200">NET-1200</a></p>
Author
Owner

@jnfrati commented on GitHub (May 27, 2026):

Moving this to a discussion as per the new issue triage flow https://github.com/netbirdio/netbird/discussions/6074

<!-- gh-comment-id:4559400427 --> @jnfrati commented on GitHub (May 27, 2026): Moving this to a discussion as per the new issue triage flow https://github.com/netbirdio/netbird/discussions/6074
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#12936