High availability
Hub survives the loss of a single Pod, node, or zone when you run hub-core,
hub-auth, the discovery aggregator, and hub-webui with multiple replicas and
spread them intentionally.
Prerequisites
- A self-hosted Hub install per the self-hosted install guide.
- A worker pool with at least three nodes spread across separate failure
domains. Node labels for
topology.kubernetes.io/zone(or an equivalent label your cluster sets) are required for zone-level anti-affinity. - An externally managed PostgreSQL with its own HA story. Hub itself goes stateless across replicas, so cluster availability is bounded by the database tier. See the database overview.
- Sized resource requests for
hub-core. Pick a tier in sizing before scaling out. Replicas without requests don't pack onto separate nodes reliably.
HA applies to hub-core, hub-auth, the discovery aggregator, and
hub-webui. hub-auth serves OIDC login and token exchange, and the aggregator
serves API discovery (/apis and /openapi), so a single replica of either is
a single point of failure. The hub-connector runs as a singleton
in each observed control plane by design and isn't horizontally scaled.
Configure replicas
Set the replica count for hub-core, hub-auth, the aggregator, and (if you
installed the UI) hub-webui:
hub-core:
api:
replicaCount: 3
aggregator:
replicaCount: 2
hub-auth:
auth:
replicaCount: 2
hub-webui:
replicaCount: 2
Three hub-core replicas is the smallest count that survives a node drain while
keeping a quorum of Ready Pods serving traffic. hub-core is stateless (every
replica reads and writes the same PostgreSQL backend), so you can scale up
further without coordination. The UI is a static asset server. Two replicas are
enough for redundancy.
Configure pod disruption budgets
The chart renders a PodDisruptionBudget for hub-core, but leaves it off by
default. Enable it once you run more than one replica so voluntary disruptions
(node drains, cluster upgrades) can't evict every replica at once:
hub-core:
api:
pdb:
create: true
minAvailable: 2
The hub-webui chart carries the same budget. Enable it if you scaled the UI.
The hub-auth chart and the aggregator don't render a PodDisruptionBudget.
hub-webui:
pdb:
create: true
minAvailable: 1
See the Kubernetes PodDisruptionBudget docs for the full field reference.
Configure anti-affinity
Spread replicas across nodes and zones so a single failure domain can't take down every Pod:
hub-core:
api:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/name: hub-core
app.kubernetes.io/instance: hub
app.kubernetes.io/component: api
topologyKey: kubernetes.io/hostname
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app.kubernetes.io/name: hub-core
app.kubernetes.io/instance: hub
app.kubernetes.io/component: api
topologyKey: topology.kubernetes.io/zone
The required term keeps two hub-core Pods off the same node. The
app.kubernetes.io/component: api label matters: the migration Job's Pod
carries the same name and instance labels and inherits
hub-core.api.affinity, so without the component label the required term
also keeps the migration Pod off every node that runs hub-core.
A required hostname term needs a free node for every rollout. The default
update strategy (maxSurge: 1, maxUnavailable: 0) starts a new Pod before it
stops an old one, so with as many replicas as nodes the new Pod can't schedule
and the rollout stalls. Run more nodes than hub-core replicas, or replace old
Pods before starting new ones:
hub-core:
api:
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
The preferred term steers the scheduler toward different zones when capacity allows, without making the Pods unschedulable if a zone is full. If you operate in a single zone, drop the preferred term. See the Kubernetes affinity and anti-affinity docs for alternative topology keys.
Next step
- Autoscaling. Let
hub-coregrow and shrink with load instead of running a fixed replica count.