---
title: "xDS control planes"
url: "/docs/operate/xds-control-planes/"
kind: "feature"
summary: "Connect Arion to an xDS control plane over delta ADS and update listeners, routes, clusters, endpoints and secrets at runtime."
---

> Arion site index: [/llms.txt](/llms.txt)

# xDS control planes

Connect Arion to an xDS control plane over delta ADS and update listeners, routes, clusters, endpoints and secrets at runtime.

An xDS control plane pushes configuration to Arion over gRPC while it runs, so listeners, routes, clusters, endpoints and secrets change without restarts. Use it when configuration changes often or is generated by a platform; a static file is fine otherwise.

The [Arion control plane](@/docs/operate/arion-control-plane.md) supplies this configuration from YAML files, an interactive Elixir shell, application code, or Kubernetes Gateway API. The connection instructions below apply to any compatible delta ADS server.

## Enable it

The bootstrap needs two things: a static cluster pointing at your control plane, and `dynamic_resources` naming that cluster for ADS. The control plane speaks gRPC, so the cluster must be HTTP/2:

```yaml,name=arion.yaml,arion=fragment
envoy_bootstrap:
  node:
    id: "arion-1"
    cluster: "edge"
  dynamic_resources:
    ads_config:
      grpc_services:
        - envoy_grpc:
            cluster_name: xds_cluster
  static_resources:
    clusters:
      - name: xds_cluster
        connect_timeout: 0.25s
        type: STATIC
        lb_policy: ROUND_ROBIN
        typed_extension_protocol_options:
          envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
            "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
            explicit_http_config:
              http2_protocol_options: {}
        load_assignment:
          cluster_name: xds_cluster
          endpoints:
            - lb_endpoints:
                - endpoint:
                    address:
                      socket_address: { address: 127.0.0.1, port_value: 50051 }
```

`node.id` and `node.cluster` identify this instance to the control plane (both are required when `node` is present). With the Arion control plane, `node.cluster` selects the fleet: this example needs resources in the `edge` fleet, so set `metadata.fleet: edge` in its control-plane manifests. Leave `api_type` and `transport_api_version` out — Arion rejects them, because there is nothing to choose: the transport is always the one described next.

## What the transport is

Arion speaks **incremental (delta) xDS over ADS**: one `AggregatedDiscoveryService` stream to one control plane, using `DeltaDiscoveryRequest`/`DeltaDiscoveryResponse`. State-of-the-world responses and per-type config sources (`lds_config`, `cds_config`) are not supported, so your control plane must serve the delta ADS variant (go-control-plane and most modern servers do). On connect, Arion subscribes to secrets, clusters, endpoints, listeners and routes in that order.

## What can be dynamic

| API | Resource | Notes |
| --- | --- | --- |
| LDS | `Listener` | Add, replace, remove listeners live. |
| RDS | `RouteConfiguration` | Referenced from an HCM via `rds` with `config_source: { ads: {} }`. |
| CDS | `Cluster` | Whole clusters. |
| EDS | `ClusterLoadAssignment` | Endpoints within a cluster. |
| SDS | `Secret` | Certificates and validation contexts — see [secrets and rotation](@/docs/security/secrets-and-rotation.md). |
| TDS | MCP `Tool`, `OpenApiToolSource`, `DynamicMcpServer`, `Toolkit` | An Arion extension for the [MCP gateway](@/docs/mcp-gateway/overview.md). |

Field-level support matches the file format exactly; the same conversion runs on both paths. See the [xDS resource type reference](@/reference/xds/resource-types.md).

## When an update is rejected

Every pushed resource goes through the same decoding and conversion as a bootstrap resource. If any resource fails this step, including an unsupported Envoy field or enum variant, Arion **NACKs** the whole response before applying its updates or removals. The reply carries an `error_detail` naming the rejected resources and reasons.

After conversion, resources are applied individually. A failure at this stage produces a NACK, but successful changes from the same response stay applied. Updates across resource types are separate responses, with no atomic transaction between them. Inspect the applied state and verify traffic after a NACK; watch the Arion log for `rejecting configs with nack response` and your control plane's NACK metrics.

## Reconnects

If the control plane is unreachable at startup, Arion retries the connection every 10 seconds (`Retrying XDS connection` in the log). If an established stream fails, the client reconnects with exponential backoff from 1 second up to 20 seconds, re-subscribes, and resumes from its tracked resource versions. Static resources keep serving throughout.

## Verify

```bash
grep -E "received config resources|Retrying XDS" /var/log/arion/arion.log
curl -s http://127.0.0.1:9901/listeners
```

Expected: `received config resources from xDS` lines as pushes arrive (a repeating `Retrying XDS connection` means the control plane cannot be reached), and the pushed listeners appearing in the [admin interface](@/docs/observe/admin-interface.md) — `/config_dump` shows the full applied state on a `config-dump` build.

## Limits

- One ADS stream at a time. Listing several `grpc_services` entries gives fallback clusters to try in turn when connecting; `google_grpc` targets are not supported here.
- The control-plane connection is an ordinary upstream cluster: endpoints load-balance round-robin, and a TLS transport socket on `xds_cluster` encrypts it.
- There is no on-demand or REST xDS, and no state-of-the-world fallback for control planes without delta support.
