Skip to main content

High availability

Hub survives the loss of a single Pod, node, or zone when you run hub-core 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 and hub-webui. 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 (and, if you installed the UI, for hub-webui):

hub-core:
api:
replicaCount: 3

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:

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
topologyKey: kubernetes.io/hostname
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app.kubernetes.io/name: hub-core
app.kubernetes.io/instance: hub
topologyKey: topology.kubernetes.io/zone

The required term keeps two hub-core Pods off the same node. 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-core grow and shrink with load instead of running a fixed replica count.