BGP Route Health Injection Setup
This guide walks through configuring Nexus GSLB to inject and withdraw BGP routes based on health check results. It covers three common deployment scenarios, peer configuration examples for FRR (Linux) and Cisco IOS, and troubleshooting steps.
For the full field reference see BGP Configuration Reference.
Concepts
Why BGP RHI alongside DNS GSLB?
DNS-based load balancing is bounded by TTL: clients that cached a response before a failure keep hitting the dead endpoint until the TTL expires. BGP RHI operates at the network layer — when a prefix is withdrawn, the upstream router immediately stops forwarding traffic to that GSLB node, regardless of what DNS clients cached. The two mechanisms complement each other:
| Layer | Mechanism | Failover speed |
|---|---|---|
| Network | BGP prefix withdrawal | Seconds (BGP convergence) |
| DNS | TTL expiry + new answer | Minutes (TTL dependent) |
Node prefixes vs pool prefixes
- Node prefixes are announced as long as the daemon is alive. Use them for the IP address of the GSLB service itself in an anycast setup — multiple nodes advertise the same prefix, BGP ECMP distributes DNS queries across them, and a failed node withdraws so its traffic shifts to survivors.
- Pool prefixes are tied to the health of a specific pool's members. Use them when your backend application servers run on an anycast or shared IP range and you want to withdraw a route when too many backends are down.
Scenario 1 — Single node, anycast VIP for the GSLB service
The simplest case: one Nexus node announces a VIP for its DNS service. If the daemon stops, the VIP disappears and clients fall through to another resolver.
Network topology:
Internet clients │ DNS queries to 203.0.113.1 ▼ Router (ASN 65000) 10.0.0.254 │ BGP session ▼ Nexus node (ASN 65001) 10.0.0.1 ──announces──▶ 203.0.113.0/24
gslbd.yaml:
<span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="fu">bgp</span><span class="kw">:</span></span><span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">enabled</span><span class="kw">:</span><span class="at"> </span><span class="ch">true</span></span><span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">localASN</span><span class="kw">:</span><span class="at"> </span><span class="dv">65001</span></span><span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">routerID</span><span class="kw">:</span><span class="at"> </span><span class="st">"10.0.0.1"</span></span><span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">listenPort</span><span class="kw">:</span><span class="at"> </span><span class="dv">179</span></span><span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">holdTime</span><span class="kw">:</span><span class="at"> </span><span class="st">"90s"</span></span><span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">keepAliveTime</span><span class="kw">:</span><span class="at"> </span><span class="st">"30s"</span></span><span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">peers</span><span class="kw">:</span></span><span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">remoteASN</span><span class="kw">:</span><span class="at"> </span><span class="dv">65000</span></span><span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">remoteAddr</span><span class="kw">:</span><span class="at"> </span><span class="st">"10.0.0.254"</span></span><span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">nodePrefixes</span><span class="kw">:</span></span><span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">"203.0.113.0/24"</span></span>
The daemon announces 203.0.113.0/24 immediately after
the BGP session establishes. On systemctl stop gslbd or
kill -TERM, GoBGP sends a BGP NOTIFICATION to the peer
before exit, which causes the peer to withdraw the route within
seconds.
Scenario 2 — Three-node anycast cluster (ECMP)
Three Nexus nodes each announce the same prefix. The upstream router load-balances DNS queries across all three via ECMP. If one node fails, it withdraws its advertisement and the remaining two absorb its traffic.
Network topology:
Router (ASN 65000)┌────────────────┐│ ECMP table ││ 203.0.113.0/24 │◄── lon-01 (10.100.0.1)│ 203.0.113.0/24 │◄── eu-01 (10.100.0.2)│ 203.0.113.0/24 │◄── lab-01 (10.100.0.3)└────────────────┘
Each node has an identical bgp: block except for
routerID, which must be unique per node.
gslbd.yaml on lon-01:
<span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">node</span><span class="kw">:</span></span><span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">id</span><span class="kw">:</span><span class="at"> </span><span class="st">"lon-01"</span></span><span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span><span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="fu">bgp</span><span class="kw">:</span></span><span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">enabled</span><span class="kw">:</span><span class="at"> </span><span class="ch">true</span></span><span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">localASN</span><span class="kw">:</span><span class="at"> </span><span class="dv">65001</span></span><span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">routerID</span><span class="kw">:</span><span class="at"> </span><span class="st">"10.100.0.1"</span><span class="co"> # unique per node</span></span><span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">listenPort</span><span class="kw">:</span><span class="at"> </span><span class="dv">179</span></span><span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">holdTime</span><span class="kw">:</span><span class="at"> </span><span class="st">"90s"</span></span><span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">keepAliveTime</span><span class="kw">:</span><span class="at"> </span><span class="st">"30s"</span></span><span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">peers</span><span class="kw">:</span></span><span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">remoteASN</span><span class="kw">:</span><span class="at"> </span><span class="dv">65000</span></span><span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">remoteAddr</span><span class="kw">:</span><span class="at"> </span><span class="st">"10.100.0.254"</span><span class="co"> # upstream router</span></span><span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">nodePrefixes</span><span class="kw">:</span></span><span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">"203.0.113.0/24"</span><span class="co"> # shared anycast prefix</span></span>
gslbd.yaml on eu-01:
<span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="fu">node</span><span class="kw">:</span></span><span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">id</span><span class="kw">:</span><span class="at"> </span><span class="st">"eu-01"</span></span><span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span><span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="fu">bgp</span><span class="kw">:</span></span><span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">enabled</span><span class="kw">:</span><span class="at"> </span><span class="ch">true</span></span><span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">localASN</span><span class="kw">:</span><span class="at"> </span><span class="dv">65001</span></span><span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">routerID</span><span class="kw">:</span><span class="at"> </span><span class="st">"10.100.0.2"</span></span><span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">listenPort</span><span class="kw">:</span><span class="at"> </span><span class="dv">179</span></span><span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">holdTime</span><span class="kw">:</span><span class="at"> </span><span class="st">"90s"</span></span><span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">keepAliveTime</span><span class="kw">:</span><span class="at"> </span><span class="st">"30s"</span></span><span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">peers</span><span class="kw">:</span></span><span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">remoteASN</span><span class="kw">:</span><span class="at"> </span><span class="dv">65000</span></span><span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">remoteAddr</span><span class="kw">:</span><span class="at"> </span><span class="st">"10.100.0.254"</span></span><span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">nodePrefixes</span><span class="kw">:</span></span><span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">"203.0.113.0/24"</span></span>
!!! tip "Router ID must be unique" Even though all nodes share the
same ASN (localASN: 65001) for iBGP designs, the
routerID must differ per node. Using the node's management
or loopback IP is the standard approach.
!!! warning "ECMP hash and sticky sessions" ECMP distributes flows by 5-tuple hash. DNS over UDP is stateless so this is fine. If you run the Nexus API or HTTPS over the anycast VIP, TCP sessions may hash differently after a node failure — consider session affinity at the application layer if needed.
Scenario 3 — Pool-driven prefix injection
Backend application servers share an anycast IP range. Nexus injects the prefix when the pool is healthy and withdraws it when too many backends fail, forcing traffic away from a degraded site.
Example: two data centres each run their own Nexus
node. Site A's pool covers 198.51.100.0/24. When Site A's
backends drop below 50% healthy, Nexus withdraws the prefix and all
traffic routes to Site B.
gslbd.yaml on Site A:
<span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="fu">bgp</span><span class="kw">:</span></span><span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">enabled</span><span class="kw">:</span><span class="at"> </span><span class="ch">true</span></span><span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">localASN</span><span class="kw">:</span><span class="at"> </span><span class="dv">65100</span></span><span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">routerID</span><span class="kw">:</span><span class="at"> </span><span class="st">"10.1.0.1"</span></span><span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">listenPort</span><span class="kw">:</span><span class="at"> </span><span class="dv">179</span></span><span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">holdTime</span><span class="kw">:</span><span class="at"> </span><span class="st">"90s"</span></span><span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">keepAliveTime</span><span class="kw">:</span><span class="at"> </span><span class="st">"30s"</span></span><span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">peers</span><span class="kw">:</span></span><span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">remoteASN</span><span class="kw">:</span><span class="at"> </span><span class="dv">65000</span></span><span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">remoteAddr</span><span class="kw">:</span><span class="at"> </span><span class="st">"10.1.0.254"</span></span><span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">poolPrefixes</span><span class="kw">:</span></span><span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">poolID</span><span class="kw">:</span><span class="at"> </span><span class="st">"site-a-web"</span><span class="co"> # ID from GET /api/v1/pools</span></span><span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">prefixes</span><span class="kw">:</span></span><span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">"198.51.100.0/24"</span></span><span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">withdrawThreshold</span><span class="kw">:</span><span class="at"> </span><span class="fl">0.5</span><span class="co"> # withdraw if <50% healthy</span></span>
Finding pool IDs:
Pool IDs are assigned when pools are created via the API. Retrieve them:
<span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co"># via REST API</span></span><span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="ex">curl</span> <span class="at">-s</span> <span class="at">-H</span> <span class="st">"Authorization: Bearer </span><span class="va">$TOKEN</span><span class="st">"</span> http://localhost:8080/api/v1/pools <span class="dt">\</span></span><span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">|</span> <span class="ex">jq</span> <span class="st">'.[] | {id, name}'</span></span><span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a></span><span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="co"># via gslbctl TUI — navigate to Pools tab, the ID column shows the UUID</span></span><span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="ex">gslbctl</span></span>
Multiple pools, multiple prefixes:
<span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="fu">bgp</span><span class="kw">:</span></span><span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">poolPrefixes</span><span class="kw">:</span></span><span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">poolID</span><span class="kw">:</span><span class="at"> </span><span class="st">"api-prod"</span></span><span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">prefixes</span><span class="kw">:</span></span><span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">"203.0.113.128/25"</span></span><span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">withdrawThreshold</span><span class="kw">:</span><span class="at"> </span><span class="fl">0.33</span><span class="co"> # withdraw if fewer than 1-in-3 healthy</span></span><span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">poolID</span><span class="kw">:</span><span class="at"> </span><span class="st">"web-prod"</span></span><span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">prefixes</span><span class="kw">:</span></span><span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">"203.0.113.0/25"</span></span><span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">withdrawThreshold</span><span class="kw">:</span><span class="at"> </span><span class="fl">0.5</span></span><span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">poolID</span><span class="kw">:</span><span class="at"> </span><span class="st">"cdn-origin"</span></span><span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">prefixes</span><span class="kw">:</span></span><span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">"198.51.100.0/24"</span></span><span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">"198.51.101.0/24"</span><span class="co"> # multiple prefixes per pool</span></span><span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">withdrawThreshold</span><span class="kw">:</span><span class="at"> </span><span class="dv">0</span><span class="co"> # any healthy endpoint = keep routes up</span></span>
Scenario 4 — Combined: node VIP + pool prefixes
The most complete setup: the node VIP keeps DNS reachable via anycast, and pool prefixes inject/withdraw based on backend health.
<span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="fu">bgp</span><span class="kw">:</span></span><span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">enabled</span><span class="kw">:</span><span class="at"> </span><span class="ch">true</span></span><span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">localASN</span><span class="kw">:</span><span class="at"> </span><span class="dv">65001</span></span><span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">routerID</span><span class="kw">:</span><span class="at"> </span><span class="st">"10.0.0.1"</span></span><span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">listenPort</span><span class="kw">:</span><span class="at"> </span><span class="dv">179</span></span><span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">holdTime</span><span class="kw">:</span><span class="at"> </span><span class="st">"90s"</span></span><span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">keepAliveTime</span><span class="kw">:</span><span class="at"> </span><span class="st">"30s"</span></span><span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">peers</span><span class="kw">:</span></span><span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">remoteASN</span><span class="kw">:</span><span class="at"> </span><span class="dv">65000</span></span><span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">remoteAddr</span><span class="kw">:</span><span class="at"> </span><span class="st">"10.0.0.254"</span></span><span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">nodePrefixes</span><span class="kw">:</span></span><span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">"203.0.113.0/24"</span><span class="co"> # DNS anycast VIP — always up</span></span><span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">poolPrefixes</span><span class="kw">:</span></span><span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">poolID</span><span class="kw">:</span><span class="at"> </span><span class="st">"web-prod"</span></span><span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">prefixes</span><span class="kw">:</span></span><span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">"198.51.100.0/24"</span><span class="co"> # app anycast VIP — health-driven</span></span><span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">withdrawThreshold</span><span class="kw">:</span><span class="at"> </span><span class="fl">0.5</span></span>
Peer configuration examples
FRR (Linux)
Install FRR and enable BGP:
<span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="ex">apt</span> install frr</span><span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="co"># edit /etc/frr/daemons, set bgpd=yes</span></span><span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="ex">systemctl</span> restart frr</span>
/etc/frr/frr.conf:
frr defaults traditionalhostname routerlog syslog informational!router bgp 65000 bgp router-id 10.0.0.254 no bgp ebgp-requires-policy ! neighbor 10.0.0.1 remote-as 65001 neighbor 10.0.0.1 description nexus-lon-01 ! address-family ipv4 unicast neighbor 10.0.0.1 activate neighbor 10.0.0.1 soft-reconfiguration inbound exit-address-family!
Verify:
<span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ex">vtysh</span> <span class="at">-c</span> <span class="st">"show bgp summary"</span></span><span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="ex">vtysh</span> <span class="at">-c</span> <span class="st">"show bgp ipv4 unicast"</span></span>
Expected output when Nexus is up:
Neighbor V AS MsgRcvd MsgSent Up/Down State/PfxRcd10.0.0.1 4 65001 8 6 00:02:14 1
FRR with MD5 authentication
Add the password field to the Nexus peer config and a
matching password statement in FRR:
gslbd.yaml:
<span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="fu">bgp</span><span class="kw">:</span></span><span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">peers</span><span class="kw">:</span></span><span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">remoteASN</span><span class="kw">:</span><span class="at"> </span><span class="dv">65000</span></span><span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">remoteAddr</span><span class="kw">:</span><span class="at"> </span><span class="st">"10.0.0.254"</span></span><span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">password</span><span class="kw">:</span><span class="at"> </span><span class="st">"s3cr3t-bgp-pw"</span></span>
/etc/frr/frr.conf:
neighbor 10.0.0.1 remote-as 65001neighbor 10.0.0.1 password s3cr3t-bgp-pw
BIRD 2
/etc/bird/bird.conf:
router id 10.0.0.254; protocol kernel { ipv4 { export all; };} protocol device {} protocol bgp nexus_lon01 { local 10.0.0.254 as 65000; neighbor 10.0.0.1 as 65001; description "Nexus GSLB lon-01"; ipv4 { import all; export none; };}
Verify:
<span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="ex">birdc</span> show protocols nexus_lon01</span><span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="ex">birdc</span> show route</span>
Cisco IOS
router bgp 65000 neighbor 10.0.0.1 remote-as 65001 neighbor 10.0.0.1 description nexus-gslb ! address-family ipv4 unicast neighbor 10.0.0.1 activate no auto-summary no synchronization exit-address-family
Verify:
show bgp summaryshow bgp ipv4 unicast neighbors 10.0.0.1 received-routes
VyOS
set protocols bgp system-as 65000set protocols bgp neighbor 10.0.0.1 remote-as 65001set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast
withdrawThreshold
decision table
| Threshold | Withdraw when... | Use case |
|---|---|---|
0.0 (default) |
All endpoints are down | Maximum tolerance — keep routes up as long as a single backend responds |
0.25 |
Fewer than 25% healthy | Withdraw early to avoid an overwhelmed survivor |
0.5 |
Fewer than 50% healthy | Standard 50% threshold — balanced |
1.0 |
Any endpoint is unhealthy | Strict — all-or-nothing; withdraw if a single endpoint fails |
Example — avoid sending traffic to a nearly-dead site:
<span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="fu">poolPrefixes</span><span class="kw">:</span></span><span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">poolID</span><span class="kw">:</span><span class="at"> </span><span class="st">"web-prod"</span></span><span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">prefixes</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="st">"198.51.100.0/24"</span><span class="kw">]</span></span><span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">withdrawThreshold</span><span class="kw">:</span><span class="at"> </span><span class="fl">0.4</span><span class="co"> # withdraw if fewer than 40% of backends respond</span></span>
Graceful shutdown behaviour
When the Nexus daemon receives SIGINT or SIGTERM, the shutdown sequence is:
- DNS server stops accepting new queries.
- BGP calls
StopBgpwith a 10-second timeout. - GoBGP sends a BGP NOTIFICATION (reason: cease) to each peer.
- The peer removes all routes learned from this session from its RIB.
- The daemon exits.
From the upstream router's perspective, routes disappear within one BGP convergence cycle — typically well under a second for directly connected peers with a short hold timer.
!!! tip "Tune hold timer for faster failover" Reducing
holdTime to "9s" and
keepAliveTime to "3s" means an unresponsive
Nexus node is detected within 9 seconds (the minimum allowed by RFC
4271). This is the minimum reasonable value for reliable
convergence.
```yamlbgp: holdTime: "9s" keepAliveTime: "3s"``` For graceful planned maintenance, the SIGTERM-based shutdown is always faster than hold-timer expiry.
Troubleshooting
Session stuck in Active state
The Active state means GoBGP is attempting to connect
but the peer is not responding.
- Check that port 179 is open on this node:
ss -tlnp | grep 179 - Check that the peer's firewall allows TCP/179 inbound from this node's IP.
- Confirm
remoteAddrmatches the peer's actual listening interface. - If using MD5, ensure the password matches exactly on both sides — a mismatch causes the TCP handshake to fail silently.
Session establishes but routes are not received by the peer
- Confirm
localASNmatches what the peer expects (remote-ason the peer config). - On FRR, check
no bgp ebgp-requires-policyis set, or add explicit import/export policies. - Run
vtysh -c "show bgp ipv4 unicast neighbors 10.0.0.1 advertised-routes"to see what GoBGP is sending.
Pool prefix never announced
- Verify the
poolIDmatches exactly (it is a UUID, not the pool's name). UseGET /api/v1/poolsto check. - Confirm the pool has health checks configured and at least one member is enabled.
- Check daemon logs for
bgp: pool prefix announced— if absent, either no health data has been received yet, or the ratio is belowwithdrawThreshold. - Set
logging.level: debugto see each health sink call.
Node prefix announced but not in the upstream RIB
- Check
show bgp ipv4 unicaston the peer: is the prefix in the BGP table but not in the routing table? The peer may have a route policy rejecting it. Addno bgp ebgp-requires-policy(FRR) or an explicitnetworkstatement. - Confirm
routerIDis a valid IPv4 address reachable by the peer.
bgp.localASN must be non-zero error at
startup
The localASN field was not set or was set to zero. It is
required when bgp.enabled: true.
Binary is large after adding BGP
GoBGP embeds gRPC and protobuf, which adds approximately 16 MB to the
stripped binary. This is expected. The binary remains CGO-free and
cross-compiles for linux/amd64 with
CGO_ENABLED=0.
Security considerations
MD5 TCP authentication
BGP MD5 (RFC 2385) prevents session hijacking by signing TCP segments. It is not encryption — use it on untrusted networks, not as a substitute for proper network segmentation.
<span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="fu">bgp</span><span class="kw">:</span></span><span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">peers</span><span class="kw">:</span></span><span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">remoteASN</span><span class="kw">:</span><span class="at"> </span><span class="dv">65000</span></span><span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">remoteAddr</span><span class="kw">:</span><span class="at"> </span><span class="st">"10.0.0.254"</span></span><span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">password</span><span class="kw">:</span><span class="at"> </span><span class="st">"long-random-string-here"</span></span>
AS path filtering on the peer
Your upstream router should filter BGP announcements to only accept prefixes you own. Example FRR prefix list:
ip prefix-list NEXUS-ALLOWED seq 10 permit 203.0.113.0/24ip prefix-list NEXUS-ALLOWED seq 20 permit 198.51.100.0/24ip prefix-list NEXUS-ALLOWED seq 99 deny 0.0.0.0/0 le 32 router bgp 65000 address-family ipv4 unicast neighbor 10.0.0.1 prefix-list NEXUS-ALLOWED in
This ensures a misconfigured Nexus cannot accidentally announce prefixes it has no authority over.
Private ASN in production
If your deployment is entirely within a private network (RFC 1918 addresses, no upstream internet connectivity), private-use ASNs (64512–65534) are appropriate and do not require IANA allocation.
For deployments that peer with internet-facing routers, use a registered ASN from your Regional Internet Registry (ARIN, RIPE, APNIC, etc.).