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.
Prerequisites
Section titled “Prerequisites”- 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.
Install
Section titled “Install”helm repo add busbar https://getbusbar.github.io/helm-chartshelm repo updatehelm install busbar busbar/busbar -n busbar --create-namespace \ -f my-values.yamlThe chart’s default image is getbusbar/busbar, pinned to the chart’s appVersion (currently
1.5.3, so a plain helm install with no image.tag override already gets you the current
release). Pin your own release with --set image.tag=1.5.3.
A minimal production values file
Section titled “A minimal production values file”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}:
image: tag: "1.5.3"
# Provider keys + admin token + signing key → 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 BUSBAR_SIGNING_KEY: a-64-hex-char-key # see "Generating a signing key" below; required by `keys`
# Rendered to /etc/busbar/config.yaml. `listen` and `admin_listen` are managed by the chart.config: identity-providers: # DEFINE the provider once; the chains below reference it BY BARE NAME (1.5.3). admin-tokens: { module: admin-tokens, token: { env: BUSBAR_ADMIN_TOKEN } } auth: chain: - keys # verify busbar-minted signed virtual keys signing_key: { env: BUSBAR_SIGNING_KEY } admin_auth: [admin-tokens] providers: anthropic: api_key: { env: ANTHROPIC_KEY } # a secret reference, resolved at boot models: claude-sonnet: provider: anthropic upstream_model: claude-sonnet-4-5Generating a signing key
Section titled “Generating a signing key”auth.signing_key is required whenever auth.chain includes keys; Busbar 1.5.3 does not
auto-generate one, and boot fails without it. Generate a key with the same image before you
helm install (it must stay the same across every pod and every upgrade; rotating it revokes
every key minted under the old one):
docker run --rm getbusbar/busbar:1.5.3 --generate-signing-keyPut the printed 64-hex-char value in secrets.data.BUSBAR_SIGNING_KEY above.
Send LLM traffic to the data Service on port 8080:
kubectl -n busbar port-forward svc/busbar 8080:8080curl -s localhost:8080/healthzThe two-listener model
Section titled “The two-listener model”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, thebusbarService. 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: ClusterIssuernetworkPolicy: enabled: true admin: allowedNamespaces: ["platform"] # who may reach the admin Servicecert-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.
Known chart/binary mismatch: as published, adminTLS.enabled renders an admin_tls: { cert_file, key_file } block, but Busbar 1.5.3 expects admin_tls: { cert, key, client_ca } and rejects the
chart’s field names (unknown field 'cert_file'). Render and check before you rely on it
(helm template ... | grep -A3 admin_tls), and fall back to existingConfigMap (see
Governance below) if it doesn’t match.
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).
Governance (persistent state)
Section titled “Governance (persistent state)”By default Busbar keeps governance ledgers (keys, usage) 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. Busbar 1.5.3 itself
persists this state through a store: { module: ..., settings: { db_path: ... } } config block
(sqlite, or a signed postgres/valkey store plugin), but as of chart 0.2.7, the published
GetBusbar/helm-charts chart does not yet render a working values-file path to that schema.
Two things worth knowing before you rely on it:
- There is no
store:values key in the chart at all. Its only persistence knob today is the legacygovernance.enabled/governance.dbPath/governance.adminTokenEnvvalues, which render an old-stylegovernance: { enabled, db_path, admin_token }block, a config shape 1.5.3 refuses to boot with (it fails--validatewith a “looks like a busbar 1.x config” error). - The admin API requires an admin token regardless of persistence: the admin auth chain
defaults to
admin-tokens, whose credential Busbar reads fromBUSBAR_ADMIN_TOKEN. Put it in the Secret under that name (the chart wires it into Busbar’sauth.admin_authchain for you;helm installfails fast if it’s missing). See the example values file above.
Until the chart catches up, use existingConfigMap to supply your own 1.5.3-schema
config.yaml (a real, documented escape hatch in values.yaml) rather than the governance.*
convenience values, and provision the PVC yourself if you need sqlite durability:
existingConfigMap: busbar-config # a ConfigMap you author/manage, containing config.yamlAuthor config.yaml directly with the 1.5.3 store: schema (see
Configuration for the full field reference), mount a PVC for it if you’re
using sqlite, and track GetBusbar/helm-charts for a release that renders store: natively.
Until then, this is the only values-file path that produces a durable store Busbar 1.5.3 will
actually boot with.
The single-writer sqlite store cannot be scaled horizontally; scale it vertically, or point at a
cluster-shared postgres / valkey store plugin 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, ingress, upgrades
Section titled “Autoscaling, ingress, upgrades”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.
Verify
Section titled “Verify”kubectl -n busbar get podskubectl -n busbar exec deploy/busbar -- /busbar --validate # config parses + validateskubectl -n busbar port-forward svc/busbar 8080:8080 &curl -s localhost:8080/healthz && echo okThe 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.