DNS: BGP Reference

BGP Route Health Injection

Nexus GSLB includes an embedded BGP speaker (GoBGP v3) that injects and withdraws routes based on health check results. This lets the daemon participate directly in your network's routing fabric alongside its DNS-layer load balancing.

Two modes operate independently and can be combined:

Mode

What it does

Node prefix

Announces configured prefixes while the daemon is running; withdraws them on graceful shutdown. Used for anycast VIPs.

Pool prefix

Announces prefixes when a pool's healthy-endpoint ratio meets a threshold; withdraws them when the pool degrades below it.


Prerequisites

  • A BGP-capable upstream: FRR, BIRD, VyOS, pfSense, OPNsense, or a physical router (Cisco IOS, Juniper JunOS, Arista EOS).

  • Your own ASN, or a private-use ASN (64512–65534 for 2-byte, 4200000000–4294967294 for 4-byte).

  • IP prefixes you are authorised to announce (your own PA/PI space, or RFC 5737/RFC 6598 private ranges in test setups).

  • Layer-3 connectivity between the Nexus node and its BGP peer.

BGP RHI is designed for on-premises and colocation deployments where you control the routing infrastructure. Most public cloud VM providers do not offer BGP peering to tenants.

License: BGP route health injection requires the pro tier or above. It is disabled at startup on free and paid installations. Contact licensing@gslb.nexus to upgrade.


Configuration reference

All fields live under the bgp: key in gslbd.yaml.

bgp: enabled: true localASN: 65001 routerID: "10.0.0.1" listenAddr: "0.0.0.0" listenPort: 179 holdTime: "90s" keepAliveTime: "30s" peers: - remoteASN: 65000 remoteAddr: "10.0.0.254" password: "" nodePrefixes: - "203.0.113.0/24" poolPrefixes: - poolID: "web-prod" prefixes: - "198.51.100.0/24" withdrawThreshold: 0.5

Top-level fields

Field

Type

Default

Description

enabled

bool

false

Must be true to activate BGP. When false all other fields are ignored.

localASN

uint32

—

Required. Your autonomous system number. Private-use: 64512–65534 (2-byte) or 4200000000–4294967294 (4-byte).

routerID

string

—

Required. A stable IPv4 address used as the BGP router ID. Typically the primary loopback or management IP of this node. Must be unique across all GSLB nodes peering with the same upstream.

listenAddr

string

"0.0.0.0"

Address on which GoBGP listens for incoming BGP connections. Set to a specific interface IP to restrict peering.

listenPort

int

179

TCP port for BGP. Change only for non-standard setups (lab environments where port 179 is blocked).

holdTime

duration

"90s"

BGP hold timer negotiated with peers. If no keepalive is received within this window the session drops. RFC 4271 minimum is 3 seconds.

keepAliveTime

duration

"30s"

Interval at which keepalives are sent. Should be one third of holdTime.

peers — BGP neighbours

A list of BGP sessions to establish. Each entry describes one neighbour.

Field

Type

Required

Description

remoteASN

uint32

✓

Neighbour's AS number. Use the same ASN as localASN for iBGP; a different ASN for eBGP.

remoteAddr

string

✓

IPv4 address of the neighbour. Must be reachable from this node.

password

string

—

MD5 TCP session password. Must match the peer's configuration. Leave empty if not used.

nodePrefixes — always-on VIP announcement

A list of CIDR prefixes announced unconditionally for the lifetime of the daemon. Routes are injected immediately after BGP sessions establish and withdrawn when the daemon shuts down gracefully (SIGINT/SIGTERM triggers a BGP NOTIFICATION before exit).

Use this for anycast VIPs that represent the GSLB service itself — you want these routes up as long as this node is alive.

bgp: nodePrefixes: - "203.0.113.0/24" - "2001:db8:1::/48" # (IPv6 prefix announcement is not yet supported)

!!! note "IPv4 only" The current implementation announces IPv4 unicast routes only. IPv6 prefix support is planned for a future release.

poolPrefixes — health-driven prefix injection

A list of pool-to-prefix bindings. Each entry maps one pool (by its ID from the database) to one or more prefixes.

Field

Type

Default

Description

poolID

string

—

Required. The pool's ID as stored in the SQLite database. Find it with GET /api/v1/pools or in the TUI.

prefixes

[]string

—

Required. One or more CIDR prefixes to announce when the pool is healthy.

withdrawThreshold

float64

0.0

Fraction of endpoints that must be healthy to keep the prefix announced. 0.0 means "withdraw only when every endpoint is down." 0.5 means "withdraw when fewer than 50% of endpoints are healthy." Must be in the range 0.0–1.0.

Announcement timing: pool prefixes are never announced at startup. They are announced only after the first health probe result arrives for the pool and the healthy-endpoint ratio meets the threshold. This prevents announcing routes before health state is known.

Idempotency: repeated health events for the same pool that do not change the announce/withdraw decision produce no BGP UPDATE — only genuine state transitions generate route churn.


How pool prefix injection works

The health manager calls a sink function on every probe result. Nexus wires BGP into this chain before the database sink:

probe result
→ health.Manager (deduplication)
→ BGP sink: OnHealthChange(poolID, ip, healthy)
→ recalculate pool healthy/total ratio
→ ratio >= threshold? → AnnouncePrefix (if not already announced)
→ ratio < threshold? → WithdrawPrefix (if currently announced)
→ DB sink (persist, metrics, WebSocket broadcast)
→ alerter (webhook delivery)

The threshold check uses a simple ratio:

healthy_count / total_count >= withdrawThreshold

where total_count is the number of distinct endpoint IPs that have ever reported a health result for this pool in the current daemon lifetime. This means:

  • New pool member added while the daemon is running: total increases as soon as the first probe fires, which can temporarily push the ratio below threshold. If you add a member to a pool that is already announcing a prefix, the prefix stays announced until the new member's first probe completes.

  • Member removed from the pool (via API or DB): the endpoint's last-known state remains in the in-memory map. Restart the daemon (or wait for the probe cycle) to clear it. In practice this is not an issue because removed members stop being probed and their state is frozen.


Validation

At startup, bgp configuration is validated before the daemon enters the BGP session establishment loop. Validation errors are fatal:

Check

Error message

localASN == 0

bgp.localASN must be non-zero when BGP is enabled

routerID not a valid IP

bgp.routerID "..." is not a valid IP address

listenPort out of range

bgp.listenPort out of range: N (must be 1–65535)

holdTime not parseable

bgp.holdTime "..." is not a valid duration

keepAliveTime not parseable

bgp.keepAliveTime "..." is not a valid duration

Peer remoteASN == 0

bgp.peers[N].remoteASN must be non-zero

Peer remoteAddr not a valid IP

bgp.peers[N].remoteAddr "..." is not a valid IP

Node prefix not a valid CIDR

bgp.nodePrefixes: "..." is not a valid CIDR: ...

Pool prefix poolID empty

bgp.poolPrefixes entry is missing poolID

Pool prefix not a valid CIDR

bgp.poolPrefixes[id]: "..." is not a valid CIDR: ...

withdrawThreshold outside 0–1

bgp.poolPrefixes[id].withdrawThreshold must be 0.0–1.0


Logging

BGP events are logged through the standard structured logger. Key log lines:

INFO bgp: node prefix announced prefix=203.0.113.0/24
INFO bgp: pool prefix announced pool=web-prod prefix=198.51.100.0/24 healthy=3 total=4
WARN bgp: pool prefix withdrawn pool=web-prod prefix=198.51.100.0/24 healthy=1 total=4
INFO BGP server started localASN=65001 routerID=10.0.0.1 peers=1 nodePrefixes=1 poolPrefixes=2
INFO BGP: graceful shutdown complete

Set logging.level: debug to see GoBGP's internal FSM transitions (session state changes, UPDATE messages, keepalive timing).


Configuration in the validation table

The BGP section is included in the startup validation table:

Section

Validated fields

bgp

(when enabled) localASN non-zero; routerID valid IP; listenPort 1–65535; holdTime/keepAliveTime parseable durations; each peer remoteASN non-zero and remoteAddr valid IP; all nodePrefixes and poolPrefixes[].prefixes valid CIDRs; withdrawThreshold in 0.0–1.0


Terraform

The nexus_bgp_status data source reads live BGP state for use in Terraform plans and outputs.

data "nexus_bgp_status" "this" {}
 
output "bgp_enabled" {
value = data.nexus_bgp_status.this.enabled
}
 
output "bgp_peers" {
value = data.nexus_bgp_status.this.peers
}

Attribute

Type

Description

enabled

bool

Whether BGP is active on this node.

local_asn

number

Local autonomous system number.

router_id

string

BGP router ID.

peers

list(object)

Configured neighbours: address (string) and asn (number) per entry.

announced

list(string)

Prefixes currently announced.


Was this article helpful?