Open-Source Wikis

/

Istio

/

Systems

/

xDS server

istio/istio

xDS server

Active contributors: howardjohn, hzxuzhonghu, costinm, ramaraochavali, stevenctl

Purpose

The xDS server is the gRPC endpoint that streams configuration to Envoy sidecars, gateways, ztunnels, and waypoints. It is the largest single subsystem inside istiod and the place where Istio's translation pipeline produces its visible output.

The implementation lives entirely under pilot/pkg/xds/. The architecture document at architecture/networking/pilot.md is the canonical narrative; this page is a code-level companion.

Directory layout

pilot/pkg/xds/
├── discovery.go         # DiscoveryServer struct: state, push pipeline, run loop
├── ads.go               # ADS bidirectional gRPC handler (state-of-the-world)
├── delta.go             # Delta xDS handler
├── pushqueue.go         # Per-proxy work queue with coalescing
├── proxy_dependencies.go # ProxyNeedsPush filter
├── eventhandler.go      # Adapter from controllers to push triggers
├── monitoring.go        # Metrics
├── debug.go             # Debug HTTP server
├── debuggen.go          # Generator that returns debug-only types
├── auth.go              # Stream authentication (caCertChain → SAN)
├── cds.go / eds.go / lds.go / rds.go / sds.go / ecds.go / nds.go / pcds.go
│                        # Per-type generators (typically thin wrappers around networking/core)
├── statusgen.go         # Generator for proxy status
├── workload.go          # Workload API generator (ztunnel)
├── krtxds.go            # krt-driven generator dispatch
├── adstest.go / deltaadstest.go / adstest.go
│                        # Test helpers (start a fake server, connect a fake client)
├── filters/             # gRPC stream filters
├── endpoints/           # EDS endpoint slice computation
└── v3/                  # xDS v3 type-URL constants

Key abstractions

Symbol File Role
DiscoveryServer discovery.go The owner of the push pipeline; one instance per istiod
Connection ads.go Per-stream context: proxy, watched types, ACK state
model.Proxy pilot/pkg/model/context.go The xDS client representation (identity, labels, sidecar scope)
model.PushContext pilot/pkg/model/push_context.go Immutable snapshot used for one or more pushes
PushRequest pilot/pkg/model/push_context.go A single push: configsUpdated, full vs partial, reason
PushQueue pushqueue.go Coalescing per-proxy queue
XdsResourceGenerator pilot/pkg/model/xds.go The interface every type-URL handler implements
proxy_dependencies.ProxyNeedsPush proxy_dependencies.go Skip pushes a proxy doesn't need
WatchedResource pilot/pkg/model/xds.go Per-stream subscription state

How it works

graph TD
    sources[Service Registry / ConfigStore / CA] -->|events| EH[EventHandler]
    EH --> deb[Debouncer<br/>DebounceAfter / debounceMax]
    deb --> pcb[PushContext.Build]
    pcb -->|configsUpdated| dispatch[Dispatch]
    dispatch --> filt[ProxyNeedsPush]
    filt --> queue[PushQueue<br/>coalesce per proxy]
    queue --> handler[ADS Push handler]
    handler --> gen[Generators]
    gen --> cache[XDS cache lookup]
    cache --> resp[DiscoveryResponse]
    resp --> stream[gRPC stream]

Push triggers

Every config or service event eventually calls DiscoveryServer.ConfigUpdate(req *PushRequest). The request carries a set of (GroupVersionKind, Namespace, Name) keys describing what changed. The debouncer collapses bursts (default PILOT_DEBOUNCE_AFTER=100ms, PILOT_DEBOUNCE_MAX=10s) and then rebuilds the PushContext.

Some events bypass the debouncer:

  • EDS-only events (when enableEDSDebounce=false per type).
  • Targeted "send to this single proxy" requests (e.g. workload re-registration).

PushContext

PushContext is the input to translation. It is immutable per push: once built, it can be read concurrently lock-free. It contains:

  • Services — the merged service catalog.
  • ServiceIndex — per-namespace and per-host indexes.
  • AuthnPolicies, AuthzPolicies — security state.
  • Telemetry — telemetry attachments.
  • SidecarIndex — per-namespace sidecar scopes.
  • gatewayIndex — gateway resources.
  • EnvoyFilters — escape-hatch patches.
  • Mesh — the merged MeshConfig.

Building it is the most expensive step in the pipeline. Caching is the single biggest performance lever.

Generators and the XDS cache

A generator is a function that, given (Proxy, PushContext, ConfigsUpdated), returns a list of *discovery.Resource. There is one per top-level type:

  • CdsGenerator (cds.go) — clusters; delegates to networking/core/cluster*.go.
  • EdsGenerator (eds.go) — endpoints.
  • LdsGenerator (lds.go) — listeners; delegates to networking/core/listener*.go.
  • RdsGenerator (rds.go) — routes; delegates to networking/core/httproute*.go.
  • SdsGenerator (sds.go) — secrets, when istiod serves SDS directly (gateways).
  • NdsGenerator (nds.go) — Istio name table for in-pod DNS.
  • EcdsGenerator (ecds.go) — extension config (Wasm).
  • WorkloadGenerator (workload.go) — Istio workload API (ztunnel).
  • DebugGen (debuggen.go) — debug-only types served at /debug/....
  • StatusGen (statusgen.go) — proxy status reflection.

The xDS cache, located in pilot/pkg/networking/core/cluster_cache.go and pilot/pkg/xds/eds.go, stores already-encoded protobuf.Any payloads keyed by their inputs. The first generation pays the encoding cost; subsequent pushes are cheap. The cache key must include every input the generator reads — incorrect keys have caused multiple CVEs (e.g. CVE-2022-21679, CVE-2024-...) and are why -tags=assert enables a strict-mode that panics on key collisions.

Per-stream lifecycle

sequenceDiagram
    participant Envoy
    participant ADS as DiscoveryServer
    participant Auth as auth.go
    participant Gen as Generators
    Envoy->>ADS: gRPC StreamAggregatedResources
    ADS->>Auth: extract identity (mTLS SAN or JWT)
    ADS->>ADS: build model.Proxy
    Envoy->>ADS: DiscoveryRequest type=CDS
    ADS->>Gen: CdsGenerator.Generate(proxy, push)
    Gen-->>ADS: resources, version
    ADS-->>Envoy: DiscoveryResponse
    Envoy->>ADS: ACK / NACK with response_nonce
    Note over ADS: Connection persists; further pushes triggered<br/>by ConfigUpdate or proxy NACK rebuild
    Envoy-xADS: graceful shutdown / disconnect

ADS is implemented in ads.go (state-of-the-world) and delta.go (delta xDS — incremental). Most Envoy clients today negotiate delta if available; legacy clients still use SoTW.

NACK handling

NACKs are recorded in the Connection.NonceAcked / NonceNAcked map and surfaced via /debug/syncz. A NACK does not trigger a re-push by default — istiod assumes the proxy has the same state until the next event. proxy-status uses this to flag stuck proxies.

Authentication

Stream auth is in pilot/pkg/xds/auth.go. Identities come from:

  • mTLS SANs — the workload identity in the client cert chain (the production path on port 15012).
  • JWT — for cross-trust-domain or legacy plain-text scenarios.
  • Kubernetes Service Account — verified by the kube-apiserver TokenReview.

The accepted authenticators are configured per-port. Port 15012 typically allows mTLS or JWT; the legacy 15010 plaintext port allows JWT only.

ProxyDependencies

proxy_dependencies.ProxyNeedsPush is the filter that decides "does this push affect this proxy?". It is conservative: it must not return false for a proxy that does need an update, but it can return true unnecessarily. The function takes the (Proxy, PushRequest) and looks at configsUpdated to make decisions like:

  • A namespace-only update for namespace foo doesn't affect proxies in namespace bar unless the proxy's sidecar scope explicitly imports foo.
  • A ServiceEntry change in any namespace might affect every sidecar (because SE has cluster-wide visibility by default).

This optimization is the difference between "every change pushes to every proxy" and "every change pushes to the few proxies that care".

Integration points

  • Inputs: every controller in istiod calls ConfigUpdate when its data changes — the registry controllers (pilot/pkg/serviceregistry/), the config store (pilot/pkg/config/), the CA (when root cert changes), the trust bundle, etc.
  • Outputs: gRPC streams to Envoy sidecars, gateways, the ingress/egress gateway, ztunnels (via the workload API), and waypoint Envoys.
  • Authentication: auth.go integrates with pkg/security/authentication/ and the kube TokenReview path.
  • Metrics: dozens of pilot_xds_* metrics in monitoring.go (push time, NACKs, connections, queue depth).

Tunables

The headline runtime knobs (set via env vars; see reference/configuration.md):

Variable Default What it does
PILOT_DEBOUNCE_AFTER 100ms Wait this long after an event before pushing
PILOT_DEBOUNCE_MAX 10s Maximum debounce time when events keep arriving
PILOT_PUSH_THROTTLE 100 Max concurrent pushes
PILOT_ENABLE_EDS_DEBOUNCE true Debounce EDS-only changes too
UNSAFE_PILOT_ENABLE_RUNTIME_ASSERTIONS false Cache key collision panics — turn on in tests
PILOT_FILTER_GATEWAY_CLUSTER_CONFIG false Trim gateway cluster config to only what the gateway needs
PILOT_ENABLE_DELTA_XDS true Allow Delta xDS streams

Entry points for modification

  • New xDS type → register a Generator in discovery.go's constructor (NewDiscoveryServer) and add a debug endpoint in debug.go.
  • New cache key dependency → audit every place the cache is written. Add an entry to the key struct and an integration test that mutates the new input.
  • New stream-level metadata → most metadata flows through model.Proxy — extend the proxy build in ads.go and propagate through generators.
  • New authenticator → pkg/security/authentication/ and wire it via auth.go.

Key source files

File Purpose
pilot/pkg/xds/discovery.go The DiscoveryServer struct + push pipeline
pilot/pkg/xds/ads.go SoTW ADS handler
pilot/pkg/xds/delta.go Delta ADS handler
pilot/pkg/xds/pushqueue.go Per-proxy queue
pilot/pkg/xds/proxy_dependencies.go Push-filter
pilot/pkg/model/push_context.go The snapshot type
pilot/pkg/model/context.go The Proxy type
pilot/pkg/networking/core/cluster_cache.go The cache (cluster side; similar files for routes, listeners)
pilot/pkg/xds/debug.go All /debug/* endpoints

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

xDS server – Istio wiki | Factory