---
title: Service Discovery
---
# Service Discovery
SMG discovers workers from Kubernetes pods and keeps its worker registry in step with the cluster. Pods that match a label selector and are Ready become workers; workers whose pods are deleted, stop matching, or turn unready are drained and removed.
---
## Overview
### :material-kubernetes: Level-Triggered Reconcile
An informer keeps a local cache of pods. Every pass compares the whole cache with the registry, so a missed watch event is corrected on the next pass.
### :material-sync: Readiness-Aware Scaling
Workers are added when their pods become Ready and drained when pods scale down, terminate, or turn unready.
### :material-lan: Multi-Port Pods
A pod that runs several engine servers lists their ports in one annotation, and each port becomes its own worker.
### :material-swap-horizontal: PD Support
Separate discovery for prefill and decode workers in disaggregated deployments.
---
## How It Works
### Informer and Reconcile Loop
SMG runs a Kubernetes informer for pods. An initial LIST fills a local cache and a WATCH keeps it current. If the watch drops, the informer reconnects (with backoff after errors) and resumes from the last version it saw; it lists again only when the API server no longer has that version (HTTP 410 Gone). SMG passes the label selector to the API server, so the cache holds only candidate pods.
A reconcile pass turns the cache into the set of desired workers, compares it with the workers that discovery registered earlier, and submits `AddWorker` and `RemoveWorker` jobs to the control-plane job queue for the difference.
| Trigger | Behavior |
|---------|----------|
| Watch event | Any change to a cached pod starts a pass. Bursts, such as a rollout or a re-list, are coalesced: SMG waits 1 second, then runs one pass. |
| Periodic resync | A pass runs every 60 seconds. It reads the local cache only and makes no API calls. |
Because every pass compares complete state instead of replaying individual events, discovery converges even when events are missed:
- A pod deleted while the watch was disconnected drops out of the cache once the watch recovers, from the replayed delete event or a fresh list, and its workers are removed on the next pass.
- A registration that failed is submitted again on a later pass, because the worker is still missing from the registry.
- A worker registered for a pod that no longer exists, for example a registration that finished after its pod was deleted, is removed on the next pass.
While an add or remove job for an address is pending or running, later passes skip that address; completed and failed jobs do not block, so failures are retried.
### Which Pods Become Workers
A pod contributes workers when all of the following hold:
- Its labels match `--selector` (or a role selector in PD mode).
- It is not terminating (no `deletionTimestamp`).
- It has a pod IP.
- Its phase is `Running` and its `Ready` condition is `True`.
SMG routes to pod IPs directly, so it honors the pod's aggregate `Ready` condition, including readiness gates, the same way a Service endpoint does. Each matching pod contributes one worker per port (see [Multi-Port Pods](#multi-port-pods)). A worker's address is `