Routing: Latency Algorithm

How It Works

The latency algorithm routes DNS queries using three signals:

  1. EDNS0 Client Subnet — If your resolver forwards the client subnet (RFC 7871), Nexus extracts a /24 (IPv4) or /48 (IPv6) prefix and maps it to a country via MaxMind GeoLite2.

  2. GeoIP Country Lookup — Client country is matched against endpoint regions.

  3. Node RTT — Health probes measure TCP/HTTP round-trip time from each cluster node. A rolling average of the last 5 probes smooths noisy data.

These combine into a weighted selection:

combined_score = region_bonus + (1 / avg_rtt_ns)

Endpoints in the client's country get a region bonus; faster endpoints (lower RTT) get higher selection probability. Traffic is distributed by weighted random selection, not always sent to the single fastest endpoint.

Configuration

loadBalancer: algorithm: "latency" latency: geoipDBPath: "/var/lib/nexus/GeoLite2-Country.mmdb" regionBonusMultiplier: 2.0 # region match weighs 2x RTT weight minRTTThresholdMs: 5 # don't shift for <5ms differences fallbackRegion: "unknown"

Field

Type

Default

Description

geoipDBPath

string

"" (falls back to geoip.dbPath)

Path to MaxMind .mmdb file

regionBonusMultiplier

float

1.0

Multiplier for same-region bonus

minRTTThresholdMs

int

5

Hysteresis floor in milliseconds

fallbackRegion

string

"unknown"

Region label when GeoIP unavailable

Endpoint Regions

Each member has a region field set when creating or updating via the API:

POST /api/v1/pools/{id}/members{ "ipAddress": "10.0.1.5", "port": 80, "region": "US"}

Region codes match the ISO 3166-1 alpha-2 country codes returned by the GeoIP database (e.g. "US", "DE", "JP"). Free-form values are also supported — the match is an exact string comparison, so be consistent.

EDNS0 Support

EDNS0 CLIENT_SUBNET is opt-in and depends on your recursive resolver configuration.

Resolver

Forwards ECS

Notes

Enterprise DNS (BIND, Knot, PowerDNS)

✅ Yes

Most DNS operators enable by default

Cloudflare Resolver (1.1.1.1)

❌ No

Strips for privacy

Google Public DNS (8.8.8.8)

❌ No

Strips for privacy

Quad9

❌ Mostly no

Privacy-focused

When ECS is not available, Nexus uses remoteIP(w.RemoteAddr()) — the resolver's IP — for GeoIP lookup. For public resolvers that strip ECS, the region bonus is applied based on where the resolver is, not the client.

Detecting stripped ECS behind another resolver

RFC 7871 §11.1 requires a resolver to omit ECS entirely when the client subnet is RFC 1918/ULA private space — so if Nexus sits behind another DNS layer (a forwarder, a corporate resolver, an upstream recursive service) that itself serves private-space clients, ECS for those clients is silently stripped before it reaches Nexus, and steering falls back to that forwarder's own source IP. If that forwarder is centralized, every client behind it geo-resolves to one place regardless of where they actually are.

Nexus can't always tell "resolver never sends ECS" apart from "resolver stripped ECS for this private client", but it does flag the one case both fall into once geo data is actually unusable: when the effective client IP (ECS if present, else the transport source) is itself private/loopback/link-local, and GeoIP has nothing to place it against. When this happens:

  • gslbd_dns_private_client_ip_total (counter, no labels) increments.

  • A rate-limited (max once/minute) warning is logged: dns: effective client IP is private/loopback/link-local, GeoIP steering is blind for this query, including whether ECS was present and the raw transport source, so an operator can grep their access logs / a packet capture for that source and find the forwarder to fix.

This doesn't recover the client's real location — that's not possible once it's been stripped upstream — it's purely so the degradation is visible instead of silent.

Hysteresis

To prevent rapid endpoint switching when RTT fluctuates slightly:

  • RTT differences below minRTTThresholdMs do not trigger a traffic shift

  • Rolling average of last 5 probes smooths single-probe spikes

  • Traffic can still shift when a region-match bonus outweighs a small RTT penalty

Fallback Chain

Signal

Unavailable

Fallback

EDNS0 client subnet

No ECS option in request

Use resolver IP from RemoteAddr()

GeoIP lookup

DB not loaded or IP not found

client_region = fallbackRegion → no region bonus

Node RTT

Fewer than 3 probes collected

Endpoint excluded from weighted selection

All RTT data

No probes for any candidate

Return first healthy candidate

Distributed RTT (Multi-Node Aggregation)

In a multi-node cluster each gslbd node probes backends independently. With distributed RTT enabled, nodes share their probe results via NATS KV and the latency algorithm uses the global minimum RTT across all nodes rather than only the local node's measurement.

How it works

  1. Each publisher writes its per-endpoint RTT to the NATS KV bucket gslb_{clusterID}_rtt under the key {nodeID}/{memberIP}.

  2. Each subscriber watches the same bucket and feeds a local RTTStore cache: ip → nodeID → {rttMs, ts}.

  3. latencyWeighted() calls RTTStore.GlobalMinRTT(ip) which returns the minimum RTT across all nodes whose data is less than 3 minutes old. Falls back to local AvgRTT() when the store has no data.

Requirements

NATS JetStream must be enabled (it is required for cluster state sync anyway). The RTT KV bucket is created automatically with a 5× health-interval TTL on each node that connects.

API and WebUI

GET /api/v1/pools/{id}/status returns both fields per member:

{ "10.0.1.5": { "healthy": true, "score": 1.0, "rttMs": 4.2, // local probe RTT from this node "globalRttMs": 2.1 // minimum across all cluster nodes }}

The WebUI pool detail shows globalRttMs when available; hovers over it say "Global min RTT across cluster". When distributed RTT is disabled only rttMs is present.

Staleness handling

RTT entries older than 3 minutes are excluded from aggregation. If all entries for an IP are stale the store returns (0, false) and the local probe RTT is used as fallback.

Limitations

  • RTT is measured from cluster nodes to endpoints, not from clients. For clients far from all cluster nodes, the measured RTT may not reflect client-perceived latency.

  • EDNS0 is stripped by major public resolvers — for those clients, geography-based routing uses the resolver's IP, not the client's actual location.

  • The algorithm requires at least 3 successful health probes per endpoint before RTT data is used. During startup, traffic is routed round-robin until sufficient data is collected.


Was this article helpful?