Kubernetes¶
The chart in deploy/helm/callbus runs callbus serve with its embedding backbone. It is not
published to a chart repository yet: install it from a checkout.
helm install callbus deploy/helm/callbus -n callbus --create-namespace \
--set auth.apiKey="$CALLBUS_API_KEY"
kubectl -n callbus port-forward svc/callbus 8765:8765
The backbone¶
embedding.mode picks where the backbone runs:
| Mode | What runs | Needs |
|---|---|---|
vllm (default) |
a vLLM pod next to callbus, weights on their own volume | a GPU node (nvidia.com/gpu) |
external |
nothing: embedding.url and embedding.model name your endpoint |
an OpenAI-compatible /v1/embeddings |
bundled |
one pod with the callbus-vllm image |
a GPU node, and the image built from deploy/docker/Dockerfile.vllm and pushed to your registry (embedding.bundled.image) |
embedding.backbone names the weights: callbus's LoRA-trained Qwen3-Embedding-8B by default
(yasinyaman/callbus-emb-8b at v0.2, served as callbus-emb-v0.2), or
{repo: Qwen/Qwen3-Embedding-8B, revision: "", name: qwen3-emb} for the base model with the packaged
heads. embedding.profile sets the precision: fp8 (the default, quantized at load, about 8 GB,
served as <name>-fp8) or bf16 (about 16 GB, <name>). In our runs FP8 stays within a point of
bf16. The embedding cache is keyed by the served name, so switching profiles
re-embeds the catalogue once. The first start of a vLLM pod downloads 16 GB; the probes allow 30
minutes for it. On k3s, and on other clusters where the NVIDIA runtime is not the default, set
embedding.runtimeClassName=nvidia (and install the NVIDIA device plugin, which advertises
nvidia.com/gpu).
Your tools¶
config is the callbus.json of Serve them to agents: MCP servers
and OpenAPI base URLs and headers. An init container runs callbus ingest mcp on every start, so
helm upgrade with a new server rolls the pod and the server appears in the catalogue. Helm merges
your values into the chart's, so drop the example server explicitly (mcpServers: {time: null}).
Credentials go in a Secret listed under envFrom and are referred to as ${VAR} in the config.
OpenAPI specs are ingested with ingest.args, or with kubectl exec into the pod: the server
re-reads the catalogue without a restart.
Keys and tenants¶
At least one key is required, since the server listens on the pod network: auth.apiKey (or
auth.apiKeySecret) for one token, auth.apiKeys (or auth.apiKeysSecret, a Secret holding the
file) for tenants, each with its own sources and credentials. The Service's
names (callbus, callbus.<namespace>.svc, ...) are allowed as Host; add an ingress host to
serve.allowedHosts. Terminate TLS at the ingress.
What it creates¶
One callbus Deployment (one replica, Recreate: the index snapshot, the usage log and the learned
heads live on one ReadWriteOnce volume), its Service, a ConfigMap, a Secret when the keys are
given inline, the data volume (and a weights volume for vllm and bundled). The volumes are kept
on helm uninstall: they hold the embedding cache and the usage log. serve.args passes further
flags (--server-weight 0.2, --co-use 2, --allow-write). Prometheus scrapes /v1/metrics with
a key that reaches every source (Metrics).
Trying it¶
scripts/helm_smoke.py installs the chart on any cluster without a GPU: a fake embedding server in
the cluster, external mode, then a search, a call to a stdio MCP server inside the pod, a refused
token, the metrics, a key limited to one source, an upgrade that adds a server, and an uninstall
that keeps the data volume. On a laptop, kind gives a cluster in
Docker:
kind create cluster --name callbus
uv run python scripts/helm_smoke.py # the published image
docker build -f deploy/docker/Dockerfile -t callbus:dev . && kind load docker-image callbus:dev --name callbus
uv run python scripts/helm_smoke.py --image callbus:dev --tenants
kind delete cluster --name callbus
Check the disk before you start: the kind node image is 1.3 GB, the loaded callbus image another
0.5–0.7 GB, and the pod writes its catalogue and index on top. A Docker install with no room left
fails in a way that looks like a chart bug — the ingest init container dies with
OSError: [Errno 28] No space left on device — and on Docker Desktop a disk image that fills up can
take the rest of your images and volumes with it when the VM recovers. df -h / and
docker system df first.
The GPU modes have been checked by rendering and by the API server's validation, not yet on a GPU cluster.