Service Discovery
Service Discovery
Nexus GSLB can auto-populate pool members from external service registries. When a service registers or deregisters with the registry, Nexus adds or removes the corresponding pool member automatically — no manual API calls required.
Discovered members coexist with manually-managed members in the same pool.
Supported registries
| Registry | Status | License requirement |
|---|---|---|
| HashiCorp Consul | Supported | Paid tier or above |
| etcd | Supported | Pro tier or above |
Discovery is disabled at startup for registries whose tier requirement is not met. Contact licensing@gslb.nexus to upgrade.
Consul
Prerequisites
- A running Consul agent or cluster reachable from the gslbd node(s).
- One or more services registered in Consul with an address and port.
- Optional: Consul ACL token with
service:readpermission.
Configuration
discovery:
consul:
addr: "http://127.0.0.1:8500" # Consul HTTP API address
token: "" # ACL token (empty = anonymous)
datacenter: "" # empty = agent default datacenter
tls:
caFile: "" # CA cert for HTTPS verification
certFile: "" # client cert (mTLS)
keyFile: "" # client key (mTLS)
watches:
- service: "web" # Consul service name to watch
poolID: "web-prod" # Nexus pool to populate
tag: "" # filter by Consul tag (empty = any)
onlyPassing: true # only include services with all checks passing
defaultPort: 0 # use if service port is 0 in Consul
defaultWeight: 1 # weight for members without Consul weights
region: "" # region override (empty = Consul node metadata "region", then datacenter)How it works
- On startup, gslbd connects to the Consul HTTP API and performs an initial fetch of all registered instances for each watched service.
- It then holds a blocking query
(
?index=X&wait=60s) — Consul blocks the request until the service catalog changes, then returns immediately with the updated list. - On each update, gslbd reconciles the pool:
- New instances →
CreateMember(tagged withsource: consul) - Changed instances (weight, region, name) →
UpdateMember - Gone instances →
DeleteMember
- New instances →
- If Consul is unreachable, the watcher retries with exponential backoff (1 s → 30 s maximum). Existing members remain untouched during the outage.
Member properties
| Member field | Source |
|---|---|
ipAddress |
Service.Address, fallback to
Node.Address |
port |
Service.Port, fallback to defaultPort |
weight |
Service.Weights.Passing, fallback to
defaultWeight |
region |
watch.region override →
Node.Meta["region"] → Node.Datacenter |
name |
{Node.Node}/{Service.ID} |
source |
"consul" |
priority |
Always 0 (equal priority) |
enabled |
Always true |
Health filtering
When onlyPassing: true (the default), Nexus passes
?passing=true to Consul's health API. This means only
service instances where all registered health checks
are passing are returned. Instances in warning or
critical state are excluded from the pool
automatically.
When onlyPassing: false, all registered instances are
returned regardless of check state. Nexus's own health checker (if
configured) will still probe them independently.
Example: two pools from one Consul cluster
discovery:
consul:
addr: "http://consul.internal:8500"
token: "a1b2c3d4-..."
watches:
- service: "web-frontend"
poolID: "frontend-pool"
tag: "production"
onlyPassing: true
defaultWeight: 10
- service: "api-backend"
poolID: "api-pool"
onlyPassing: true
defaultWeight: 5
region: "us-east-1" # force region for all api-backend instancesCombining with manual members
Discovered members are tagged with source: "consul". You
can add manually-managed members to the same pool — they will have
source: "" and are never touched by the discovery
reconciler.
Visibility
- API:
GET /api/v1/pools/{poolId}/members— returned members include"source": "consul". - WebUI: Pool detail page shows a
consulbadge next to discovered member entries. - TUI: Member list shows
[consul]suffix after discovered entries. - gslbctl:
gslbctl members list --pool <id>— no change needed; source field is visible in the raw output.
Permissions
Discovery runs with the daemon's own DB access — no API key or user
permission is required. The Consul ACL token is configured in
gslbd.yaml only.
etcd
Prerequisites
- A running etcd v3 cluster reachable from the gslbd node(s) via its HTTP gateway (default port 2379).
- One or more keys registered under a common prefix, each containing a
JSON value with at minimum
ipandport. - Optional: etcd username/password for authentication, or client TLS certificates.
Configuration
discovery:
etcd:
endpoints:
- "http://127.0.0.1:2379" # etcd HTTP gateway address (only first endpoint is used)
username: "" # authentication username (empty = no auth)
password: "" # authentication password
tls:
caFile: "" # CA cert for HTTPS verification
certFile: "" # client cert (mTLS)
keyFile: "" # client key (mTLS)
watches:
- prefix: "/nexus/members/web-prod/" # KV key prefix to watch
poolID: "web-prod" # Nexus pool to populate
defaultWeight: 1 # weight for members without a weight in their value| Field | Default | Description |
|---|---|---|
endpoints |
— | List of etcd HTTP gateway URLs. Only the first entry is used. |
username |
"" |
Username for etcd authentication. Leave empty to skip authentication. |
password |
"" |
Password for etcd authentication. |
tls.caFile |
"" |
Path to a CA certificate file for verifying the etcd server's TLS certificate. |
tls.certFile |
"" |
Path to a client certificate for mTLS authentication. |
tls.keyFile |
"" |
Path to the client certificate's private key. |
watches[].prefix |
— | Required. The key prefix to scan and watch. All keys with this prefix are treated as pool members. |
watches[].poolID |
— | Required. The Nexus pool to populate. |
watches[].defaultWeight |
1 |
Weight applied to members whose JSON value does not include a
weight field, or where weight is 0. |
Value format
Each key under the watched prefix must have a JSON value:
{"ip": "10.0.0.1", "port": 443, "weight": 10, "region": "us-east"}| Field | Required | Description |
|---|---|---|
ip |
✓ | IP address of the member. |
port |
✓ | Port number. Must be non-zero. |
weight |
— | Routing weight. Falls back to defaultWeight when absent
or 0. |
region |
— | Region label for geo-aware routing. Defaults to
"unknown" when absent. |
Keys whose value is missing ip or has port
equal to 0 are silently skipped.
How it works
- On startup, gslbd authenticates with etcd (if credentials are
configured) via the etcd v3
/v3/auth/authenticateendpoint. - It performs an initial key range fetch using
/v3/kv/rangefor each watched prefix and reconciles the pool. - gslbd then opens a streaming watch via the
/v3/watchHTTP endpoint. The stream delivers change events for the key range as they occur. - On each batch of events received, gslbd performs a full
re-fetch of the key range and reconciles the pool:
- New keys →
CreateMember(tagged withsource: etcd) - Changed keys (weight, region, port) →
UpdateMember - Deleted keys →
DeleteMember
- New keys →
- If the connection fails, the watcher retries with exponential backoff starting at 1 s, capping at 30 s. Existing members remain untouched during the outage.
Member properties
| Member field | Source |
|---|---|
ipAddress |
ip field in the JSON value |
port |
port field in the JSON value |
weight |
weight field in the JSON value; fallback to
defaultWeight (default 1) |
region |
region field in the JSON value; fallback to
"unknown" |
name |
Full etcd key string (e.g.
/nexus/members/web-prod/node-01) |
source |
"etcd" |
priority |
Always 0 (equal priority) |
enabled |
Always true |
Example: auto-register application nodes
discovery:
etcd:
endpoints:
- "https://etcd.internal:2379"
tls:
caFile: "/etc/ssl/etcd-ca.pem"
watches:
- prefix: "/services/web/nodes/"
poolID: "web-pool"
defaultWeight: 10
- prefix: "/services/api/nodes/"
poolID: "api-pool"
defaultWeight: 5Application nodes register themselves on startup:
etcdctl put /services/web/nodes/web-01 '{"ip":"10.0.1.10","port":80,"region":"eu-west"}'Deregistration on shutdown:
etcdctl del /services/web/nodes/web-01Combining with manual members
Discovered members are tagged with source: "etcd". You
can add manually-managed members to the same pool — they will have
source: "" and are never touched by the discovery
reconciler.
Visibility
- API:
GET /api/v1/pools/{poolId}/members— returned members include"source": "etcd". - WebUI: Pool detail page shows an
etcdbadge next to discovered member entries. - TUI: Member list shows
[etcd]suffix after discovered entries. - gslbctl:
gslbctl members list --pool <id>— source field is visible in the raw output.
Operational notes
Multiple cluster nodes: Each gslbd node runs its own discovery watchers independently. All nodes write to their own local SQLite DB. The result is consistent across nodes via the normal NATS state sync. This means each node maintains a correctly reconciled member list without a central coordinator.
Deregistration delay: When a Consul instance
deregisters, the gslbd watcher receives the update within the Consul
?wait=60s window. Under normal conditions the member is
removed within seconds of deregistration.
Manual override: If you delete a discovered member via the API or TUI, the discovery reconciler will re-add it on the next sync. To permanently exclude an instance, use Consul's native maintenance mode or deregister it from Consul rather than from Nexus.