Octopus
1.x
Docs/Octopus/Discovery
Open

Reading3 min
Updated31 Jul 2026
Sourcev1/kubernetes/discovery.mdx

Discovery

Inside the cluster, Octopus discovers upstream Pod endpoints from the Kubernetes API. The primary path uses the discovery.k8s.io/v1 EndpointSlice API, which reflects Pod scale up/down — events a Service-only watch would miss. The legacy Endpoints API is retained as a fallback for clusters where the EndpointSlice API is unavailable.

This is configured as a FARP discovery backend of type kubernetes. It is independent of the operator: the operator programs routes from Gateway API / Octopus CRDs, while discovery populates upstream instances.

How it works01

  • It lists Service objects (honoring the label selector) and the EndpointSlices that own them (matched via the kubernetes.io/service-name label), then groups slices by their owning (namespace, service).

  • For each Service that passed the selector, it emits one upstream instance per ready endpoint address × port. Slices of addressType: FQDN are skipped (no routable IPs).

  • A watch on EndpointSlices keeps instances current: applied/updated slices re-discover the owning Service, deleted slices deregister it. Because slices change on scale events, scaling a Deployment up or down is reflected without a Service event.

  • Each endpoint's ready condition maps to instance health: ready: true → healthy, false → unhealthy, unset → unknown.

Configuration02

Discovery is enabled under farp.discovery.backends as a kubernetes backend:

farp:
  enabled: true
  discovery:
    backends:
      - type: kubernetes
        enabled: true
        config:
          # "" = watch all namespaces (needs cluster-scoped RBAC).
          namespace: ""
          # label_selector: "app.kubernetes.io/part-of=my-platform"
          watch_interval: 30s
          use_endpoint_slices: true
          include_not_ready: false
FieldTypeDefaultDescription
namespacestring(required)Namespace to watch. Empty string watches all namespaces (requires cluster-scoped RBAC).
label_selectorstring(unset)Label selector filtering which Services are discovered.
watch_intervalduration(required)Resync/poll interval.
use_endpoint_slicesbooltruePrefer the EndpointSlice API; fall back to Endpoints if it's unavailable.
include_not_readyboolfalseAlso emit endpoints whose ready condition is false/unknown.
Note

When namespace is empty (all namespaces) the discovery client lists/watches cluster-wide; when set, it scopes the API calls to that one namespace. Single-namespace discovery only needs a namespaced Role.

include_not_ready03

By default only ready endpoints become upstream instances, so traffic is never sent to a Pod that is still starting or failing its readiness probe. Set include_not_ready: true to also surface not-ready endpoints (for example, to drive your own health-aware load balancing); they are emitted with unhealthy/unknown health rather than being dropped.

EndpointSlice vs. Endpoints fallback04

With use_endpoint_slices: true (the default), discovery runs the EndpointSlice path and only falls back to the legacy Endpoints API if that path errors (e.g. the API is not served). The EndpointSlice path is preferred because it reflects scale up/down; the Endpoints fallback lists ready addresses per Service subset and treats all listed addresses as healthy.

RBAC05

Discovery needs get/list/watch on the following. The Helm chart's RBAC already grants these:

- apiGroups: [""]
  resources: ["services", "endpoints"]   # Service metadata + legacy Endpoints fallback
  verbs: ["get", "list", "watch"]
- apiGroups: ["discovery.k8s.io"]
  resources: ["endpointslices"]          # primary discovery source
  verbs: ["get", "list", "watch"]

To watch across all namespaces you need a ClusterRole/ClusterRoleBinding (rbac.clusterScoped=true, the chart default). To restrict discovery to one namespace, set rbac.clusterScoped=false and discovery.namespace=<ns>; the chart then creates a namespaced Role/RoleBinding covering just services, endpoints, and endpointslices. You can confirm access with:

kubectl auth can-i list endpointslices \
  --as=system:serviceaccount:octopus:octopus

See Probes & drain for how the first discovery sync gates readiness, and the Helm chart page for the RBAC and discovery-scope values.