netbirdio/netbird · error
add nat postrouting jump rule: %v
Error message
add nat postrouting jump rule: %v
What it means
First insert in addJumpRules(): puts `-j NETBIRD-RT-NAT` at position 1 of the built-in nat POSTROUTING chain so masqueraded traffic enters NetBird's chain. Wrapped as 'add jump rules' from createContainers(), failure aborts router setup and leaves the custom chains created but unreachable.
Source
Thrown at client/firewall/iptables/router_linux.go:612
func (r *router) insertEstablishedRule(chain string) error {
establishedRule := getConntrackEstablished()
err := r.iptablesClient.Insert(tableFilter, chain, 1, establishedRule...)
if err != nil {
return fmt.Errorf("failed to insert established rule: %v", err)
}
ruleKey := "established-" + chain
r.rules[ruleKey] = establishedRule
return nil
}
func (r *router) addJumpRules() error {
// Jump to nat chain
natRule := []string{"-j", chainRTNAT}
if err := r.iptablesClient.Insert(tableNat, chainPOSTROUTING, 1, natRule...); err != nil {
return fmt.Errorf("add nat postrouting jump rule: %v", err)
}
r.rules[jumpNatPost] = natRule
// Jump to mangle prerouting chain
preRule := []string{"-j", chainRTPRE}
if err := r.iptablesClient.Insert(tableMangle, chainPREROUTING, 1, preRule...); err != nil {
return fmt.Errorf("add mangle prerouting jump rule: %v", err)
}
r.rules[jumpManglePre] = preRule
// Jump to nat prerouting chain
rdrRule := []string{"-j", chainRTRDR}
if err := r.iptablesClient.Insert(tableNat, chainPREROUTING, 1, rdrRule...); err != nil {
return fmt.Errorf("add nat prerouting jump rule: %v", err)
}
r.rules[jumpNatPre] = rdrRule
return nilView on GitHub (pinned to 93e97f4bf1)
Solutions
- Reproduce manually: `sudo iptables -t nat -I POSTROUTING 1 -j NETBIRD-RT-NAT`
- `modprobe iptable_nat` (ip6table_nat for the v6 router)
- Verify CAP_NET_ADMIN and that /run/xtables.lock is not held
- Check `iptables --version` family consistency (iptables-legacy vs iptables-nft) across the system
- Run `netbird down` then `netbird up` for a clean retry
Example fix
// before
if err := r.iptablesClient.Insert(tableNat, chainPOSTROUTING, 1, natRule...); err != nil {
return fmt.Errorf("add nat postrouting jump rule: %v", err)
}
// after: idempotent insert tolerant of an existing jump from a previous run
exists, _ := r.iptablesClient.Exists(tableNat, chainPOSTROUTING, natRule...)
if !exists {
if err := r.iptablesClient.Insert(tableNat, chainPOSTROUTING, 1, natRule...); err != nil {
return fmt.Errorf("add nat postrouting jump rule: %w", err)
}
}
r.rules[jumpNatPost] = natRule Defensive patterns
Strategy: validation
Validate before calling
func builtinChainWritable(ipt *iptables.IPTables, table, chain string) error {
probe := []string{"-m", "comment", "--comment", "nb-probe", "-j", "RETURN"}
if err := ipt.Insert(table, chain, 1, probe...); err != nil {
return fmt.Errorf("%s/%s: %w", table, chain, err)
}
return ipt.DeleteIfExists(table, chain, probe...)
} Try / catch
Propagate as fatal for router init; ensure the previously created custom chains get cleaned by the existing cleanup path on the way out.
Prevention
- Load iptable_nat before agent start
- Avoid mid-start firewall backend switches (legacy to nft)
- Keep CAP_NET_ADMIN on the daemon for the whole uptime, not just start
When it happens
Trigger: `iptables -t nat -I POSTROUTING 1 -j NETBIRD-RT-NAT` failing when the nat table is missing (iptable_nat unloaded), CAP_NET_ADMIN is absent, the xtables lock is held, or another manager rewrote POSTROUTING between the agent's reads and writes.
Common situations: Same setup-time class as the other jump-rule errors: module-less kernels, unprivileged containers, docker/firewalld lock contention; also hosts where an admin replaced POSTROUTING wholesale with nft rules while the agent still speaks legacy iptables.
Related errors
- add static nat rules: %w
- add jump rules: %w
- add outbound masquerade rule: %v
- add return masquerade rule: %v
- failed to insert established rule: %v
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/3afff82025225b82.
Report an issue: GitHub.