Octopus
1.x
Docs/Octopus/Discovery backends
Open

Reading6 min
Updated31 Jul 2026
Sourcev1/farp/backends.mdx

Discovery backends

A discovery backend tells FARP which service instances are live and where to reach them. Octopus then fetches each instance's published schema and generates routes — see Schema federation.

Backends are listed under farp.discovery.backends. Each entry is a tagged object with a type field (mdns, dns, consul, or kubernetes), an enabled flag (defaults to true), and a backend-specific config block. You can configure several backends at once — every enabled backend becomes a provider, and FARP polls them all on each tick of farp.watch_interval.

farp:
  enabled: true
  watch_interval: 5s
  schema_cache_ttl: 5m
  discovery:
    backends:
      - type: mdns
        enabled: true
        config:
          service_type: _octopus._tcp
          domain: local.
          watch_interval: 30s
          query_timeout: 5s
          enable_ipv6: false
      - type: consul
        enabled: true
        config:
          address: http://consul:8500
          datacenter: dc1
          watch_interval: 30s
Note

Each backend is gated behind a Cargo feature in the gateway build (mdns, dns, consul, kubernetes). The default runtime build enables only mdns. If a backend is configured but its feature is not compiled in, the gateway logs a warning and skips that backend rather than failing to start.

What a backend reports01

Every backend resolves to a list of service instances with an id, logical name, address, port, a health status (healthy / unhealthy / unknown / warning), and optional metadata (version, tags, datacenter, and a free-form custom map). FARP keys schema fetching and route generation off this metadata.

Warning

FARP only registers an instance and generates routes for it when its metadata advertises farp.enabled = true. Instances without that flag are discovered but skipped. The OpenAPI document is then fetched from farp.openapi.path (default /openapi.json); the health, metrics, AsyncAPI, gRPC reflection, and GraphQL endpoints come from the matching farp.*.path keys (with legacy unprefixed keys such as openapi / health accepted as fallbacks).

etcd is not a configurable backend02

The source tree contains an etcd discovery module, but it is not exposed through farp.discovery.backends — the configuration only accepts mdns, dns, consul, and kubernetes. There is no type: etcd entry, and the gateway runtime does not compile an etcd discovery feature. Treat etcd discovery as unavailable for now. (Separately, etcd can act as a schema registry backend for the fetcher, but that path is marked unimplemented in the source and is not configurable here.)

mDNS03

Zero-configuration discovery for local development. The gateway browses for a multicast-DNS service type (e.g. _octopus._tcp.local.) and reads service metadata from the advertised TXT records. mDNS does not support registration from the gateway side — services advertise themselves using their own mDNS library.

- type: mdns
  enabled: true
  config:
    service_type: _octopus._tcp
    domain: local.
    watch_interval: 30s
    query_timeout: 5s
    enable_ipv6: false
KeyTypeRequiredDescription
service_typestringyesmDNS service type to browse, e.g. _octopus._tcp.
domainstringyesmDNS domain, usually local..
watch_intervaldurationyesHow often to re-browse for services.
query_timeoutdurationyesHow long each browse waits to collect responses.
enable_ipv6booleanno (true)Register IPv6 addresses. When false, only IPv4 addresses from discovered services are registered.
Note

enable_ipv6: false filters which discovered addresses are registered — it does not stop the mDNS library from querying IPv6 interfaces, so harmless IPv6/VPN interface log lines may still appear. The internal default for enable_ipv6 is false on macOS and true elsewhere, but because MdnsDiscoveryConfig has no serde default for this field, set it explicitly in your YAML.

TXT-record keys are mapped onto instance metadata: version, dc/datacenter, tags (comma separated), and openapi / asyncapi / graphql / health (recorded as endpoint hints). Any other key=value pair is preserved in the custom metadata map.

Consul04

Discovery from a Consul service catalog with health filtering. The gateway calls Consul's HTTP API (/v1/catalog/services then /v1/health/service/{name}) and maps Consul health checks onto FARP health: any critical check marks the instance unhealthy, any warning check marks it warning, otherwise healthy.

- type: consul
  enabled: true
  config:
    address: http://consul:8500
    datacenter: dc1
    token: "${CONSUL_TOKEN}"
    watch_interval: 30s
KeyTypeRequiredDescription
addressstringyesBase URL of the Consul HTTP API, e.g. http://consul:8500.
datacenterstringyesDatacenter to query. Sent as the dc query parameter; pass an empty string to omit it.
tokenstringnoACL token, sent as the X-Consul-Token header.
watch_intervaldurationyesPolling interval for catalog changes.

The Consul version meta field becomes the instance version, Consul tags become instance tags, and the full service Meta map is carried through as custom metadata (so farp.enabled and the farp.* endpoint keys can be set there).

Note

The Consul HTTP client connects over plain HTTP. Front Consul with a local agent or a TLS- terminating proxy if you need encryption in transit.

DNS05

Resolve instances by name from DNS. For a queried name, the resolver tries an SRV lookup first (using the SRV target host and port), then falls back to A/AAAA records on a default port.

- type: dns
  enabled: true
  config:
    servers: ["10.0.0.53:53"]
    domain: services.internal
    watch_interval: 30s
KeyTypeRequiredDescription
serversarray of stringyesDNS server addresses. Accepted by the schema but currently ignored by the runtime (see callout).
domainstringyesDomain to query. Accepted by the schema but currently ignored by the runtime (see callout).
watch_intervaldurationyesPolling interval. This is applied.
Warning

The DNS backend is the least complete. When wiring the provider, the runtime only forwards watch_interval — the servers and domain fields you set are not applied, and the resolver uses the host's system DNS configuration with a hardcoded default port of 80. The provider also cannot enumerate services (it resolves a known name on demand), so it does not return a service list during FARP's discovery sync. Prefer mDNS, Consul, or Kubernetes for automatic route generation, and treat DNS discovery as experimental.

Kubernetes06

In-cluster discovery of Pod endpoints via the discovery.k8s.io/v1 EndpointSlice API, falling back to the legacy Endpoints API when EndpointSlices are unavailable. This backend is documented in depth in the Kubernetes section, including RBAC and how it relates to the Gateway API controller.

- type: kubernetes
  enabled: true
  config:
    namespace: default
    label_selector: "app.kubernetes.io/part-of=myapp"
    watch_interval: 10s
    use_endpoint_slices: true
    include_not_ready: false
KeyTypeRequiredDescription
namespacestringyesNamespace to watch. An empty string watches all namespaces.
label_selectorstringnoLabel selector to filter Services/Pods.
watch_intervaldurationyesPolling interval.
use_endpoint_slicesbooleanno (true)Prefer the EndpointSlice API; falls back to Endpoints if unavailable.
include_not_readybooleanno (false)Include endpoints whose ready condition is false/unknown.
Note

EndpointSlices reflect Pod scale up/down that a legacy Endpoints + Service watch can miss. See Kubernetes discovery for cluster setup, RBAC, and the full configuration reference.

Where to go next07