Skip to content

Deploy on Kubernetes (Helm)

Busbar ships an official Helm chart. It renders the Deployment, the two Services Busbar needs (the public data plane and the separate admin plane), the config ConfigMap, and the Secret wiring for provider keys, with sensible, secure defaults.

  • Kubernetes 1.24+ and Helm 3.8+.
  • cert-manager if you expose the admin plane in-cluster with mTLS (the default posture; see the two-listener model below). Not needed if you keep the admin plane loopback-only.
Terminal window
helm repo add busbar https://getbusbar.github.io/helm-charts
helm repo update
helm install busbar busbar/busbar -n busbar --create-namespace \
-f my-values.yaml

The chart’s default image is getbusbar/busbar, pinned to the chart’s appVersion. Pin your own release with --set image.tag=1.5.0.

Busbar is configured by a config.yaml (the deploy config) and providers.yaml (the provider catalog, which ships inside the image; you only override it if you add providers). The chart renders config into config.yaml, mounts it at /etc/busbar/, and injects secrets as env vars that Busbar interpolates with ${VAR}:

my-values.yaml
image:
tag: "1.5.0"
# Provider keys + admin token → a Secret. Provider keys are read via secret references
# (api_key: { env: VAR }); the admin token is read from BUSBAR_ADMIN_TOKEN.
secrets:
create: true
data:
ANTHROPIC_KEY: sk-ant-...
BUSBAR_ADMIN_TOKEN: a-long-random-string
# Rendered to /etc/busbar/config.yaml. `listen` and `admin_listen` are managed by the chart.
config:
auth:
chain:
- keys # verify busbar-minted signed virtual keys
admin_auth:
- admin-tokens: { token: { env: BUSBAR_ADMIN_TOKEN } }
providers:
anthropic:
api_key: { env: ANTHROPIC_KEY } # a secret reference, resolved at boot
models:
claude-sonnet:
provider: anthropic
upstream_model: claude-sonnet-4-5

Send LLM traffic to the data Service on port 8080:

Terminal window
kubectl -n busbar port-forward svc/busbar 8080:8080
curl -s localhost:8080/healthz

Busbar always runs its admin API (/api/v1/admin/…) on a separate listener from public LLM traffic, so the two never share a port, bind, or firewall posture.

  • Data plane: 0.0.0.0:8080, the busbar Service. Front it with an Ingress or Gateway for LLM traffic.
  • Admin plane: admin_listen, defaults to loopback. A network-exposed admin listener refuses to boot unless it requires mTLS or is explicitly marked insecure. So the chart keeps the admin plane loopback-only unless you opt in:
service:
admin:
enabled: true # expose the admin plane on an in-cluster Service (:8081)
adminTLS:
enabled: true # required for a non-loopback admin bind
certManager:
enabled: true
issuerRef:
name: busbar-admin-ca
kind: ClusterIssuer
networkPolicy:
enabled: true
admin:
allowedNamespaces: ["platform"] # who may reach the admin Service

cert-manager issues the admin server cert (and, optionally, client certs for your tooling); the NetworkPolicy restricts who can reach it. For a lab you can instead set adminInsecure: true, but never do that on a cluster reachable by untrusted workloads.

Both ports serve GET /healthz unauthenticated; the chart uses it for liveness and readiness on the data port (readiness returns 200 once at least one lane is usable).

With the default store: memory, Busbar keeps governance ledgers in ephemeral per-replica RAM: the chart runs a Deployment you can scale horizontally (breaker and health state are correctly per-replica), but keys and usage do not persist or share across replicas. To persist and share state, configure a durable store plugin (store: { module: sqlite | postgres | redis }). A single-writer sqlite store needs a PVC, so the chart switches to a StatefulSet with replicas: 1; the postgres and redis stores are cluster-shared, so several replicas can back the same store:

The admin API requires an admin token: the admin auth chain defaults to admin-tokens, whose credential Busbar reads from BUSBAR_ADMIN_TOKEN. Put it in the Secret under that name (the chart wires it into Busbar’s auth.admin_auth chain for you; helm install fails fast if it’s missing):

governance:
enabled: true
persistence:
size: 1Gi
storageClass: standard
secrets:
data:
BUSBAR_ADMIN_TOKEN: a-long-random-admin-token

The single-writer sqlite store cannot be scaled horizontally; scale it vertically, or use the cluster-shared postgres / redis store plugins to run multiple replicas against one store. Note that limit windows (requests/tokens) are per-process, so those caps enforce per node even over a shared store; budgets accrue additively to the shared store. See Running multiple instances (HA).

autoscaling:
enabled: true # HPA on the stateless data Deployment (not the governance StatefulSet)
minReplicas: 2
maxReplicas: 8
targetCPUUtilizationPercentage: 70
ingress:
enabled: true
className: nginx
hosts:
- host: llm.example.com
paths: [{ path: /, pathType: Prefix }]

Changing config and running helm upgrade updates the ConfigMap; a checksum annotation rolls the pods so the new config takes effect (set reloadOnConfigChange: false to opt out). Because the config is identical across replicas, rollouts are safe.

Terminal window
kubectl -n busbar get pods
kubectl -n busbar exec deploy/busbar -- busbar --validate # config parses + validates
kubectl -n busbar port-forward svc/busbar 8080:8080 &
curl -s localhost:8080/healthz && echo ok

The full values surface is documented in the chart’s README. For the admin API the Services expose, see the Admin API reference and the live API reference.