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:read permission.

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

  1. On startup, gslbd connects to the Consul HTTP API and performs an initial fetch of all registered instances for each watched service.
  2. 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.
  3. On each update, gslbd reconciles the pool:
    • New instances → CreateMember (tagged with source: consul)
    • Changed instances (weight, region, name) → UpdateMember
    • Gone instances → DeleteMember
  4. 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 instances

Combining 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 consul badge 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 ip and port.
  • 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

  1. On startup, gslbd authenticates with etcd (if credentials are configured) via the etcd v3 /v3/auth/authenticate endpoint.
  2. It performs an initial key range fetch using /v3/kv/range for each watched prefix and reconciles the pool.
  3. gslbd then opens a streaming watch via the /v3/watch HTTP endpoint. The stream delivers change events for the key range as they occur.
  4. On each batch of events received, gslbd performs a full re-fetch of the key range and reconciles the pool:
    • New keys → CreateMember (tagged with source: etcd)
    • Changed keys (weight, region, port) → UpdateMember
    • Deleted keys → DeleteMember
  5. 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: 5

Application 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-01

Combining 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 etcd badge 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.


Was this article helpful?
© 2026