Getting Started
Thalamus is a vendor-neutral, Kubernetes-native inference service based on llm-d, the Gateway API inference extension, and Cortex.
Prerequisites
Tools
- kubectl — Kubernetes CLI
- helm — Kubernetes package manager (v3.x)
- helmfile — declarative wrapper around helm (v1.x)
- A Kubernetes cluster with GPU nodes (NVIDIA), or minikube / any other local cluster for development
Accounts
- A Hugging Face account with a read token and access to the models you want to serve
Step 1 — Create the Hugging Face secret
Thalamus pulls model weights from Hugging Face at pod startup. Create a secret with your Hugging Face token in the thalamus namespace:
kubectl create namespace thalamusThen create the secret. The chart expects a secret named hf-token with key HF_TOKEN.
kubectl create secret generic hf-token \
--from-literal=HF_TOKEN="$HF_TOKEN" \
--namespace thalamusStep 2 — Create API key secrets (optional)
When the apikey-auth policy is enabled (gateway.policies.apikey-auth.enabled), every request to the targeted routes or listeners must carry a valid Authorization: Bearer <token> header. Tokens are stored as Kubernetes Secrets labelled thalamus-apikey: "true".
Set targetRefs on the policy to scope where auth is enforced. targetRefs default to the Gateway, so an entry only needs the fields it overrides — here, the listener section to protect:
thalamus:
gateway:
policies:
apikey-auth:
enabled: true
targetRefs:
- sectionName: https-apiCreate one secret per user or client:
kubectl create secret generic apikey-<name> \
--namespace thalamus \
--from-literal=api-key=$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')
kubectl label secret apikey-<name> --namespace thalamus thalamus-apikey=trueOpen WebUI connects to the inference API internally and also requires a token. Set the following in your cluster values to point Open WebUI at the secret:
open-webui:
openaiApiKeyExistingSecret: apikey-openwebui
openaiApiKeyExistingSecretKey: api-keyCustom gateway policies
apikey-auth and bbr are just entries in gateway.policies. Any key you add renders an AgentgatewayPolicy of the same name, with the entry used as its spec. targetRefs default to the Gateway and are merged per-entry, so you only specify overrides. Attach elsewhere with a full targetRefs entry or by label via targetSelectors:
thalamus:
gateway:
policies:
rate-limit:
targetRefs:
- kind: HTTPRoute
name: aggregated-models
traffic:
localRateLimit:
- maxTokens: 100
tokensPerFill: 100
fillInterval: 60sStep 3 — Deploy the stack
The helm/helmfile.yaml.gotmpl manifest installs the full stack as a set of ordered helmfile releases: the Gateway API and Inference Extension CRDs, the Thalamus CRDs, the GPU operator and node feature discovery, the agentgateway with its CRDs, kube-prometheus-stack for observability, the thalamus chart (operator + workloads: inference gateway, models, routes), and finally open-webui. Helmfile registers the required helm repositories and applies the releases in dependency order.
Thalamus operator — under development
The Thalamus operator will automate model instance management and move model declaration from Helm values to the
thalamus.cloud/v1alpha1 ModelCRD, enabling fully declarative, per-resource lifecycle control. Until then, models are managed through themodels:values key described below.
Deploy with chart defaults:
helmfile --file helm/helmfile.yaml.gotmpl applyTo customize values for your cluster, write a release-keyed values file and pass it via --state-values-file. The top-level keys are helmfile release names (e.g. thalamus, open-webui, gpu-operator, kube-prometheus-stack, agentgateway); everything underneath is forwarded to that release as chart values. See example.values.yaml for a reference shape of the thalamus release values.
# my-cluster.yaml
thalamus:
models:
- slug: qwen3-6-27b
model: Qwen/Qwen3.6-27B
accelerator: nvidia
resources:
requests: { nvidia.com/gpu: "2" }
limits: { nvidia.com/gpu: "2" }helmfile --file helm/helmfile.yaml.gotmpl apply \
--state-values-file my-cluster.yamlTo preview changes before applying, use helmfile diff in place of apply.
Caveats for values file:
- Adjust
resourcesfor your selected model. If it fails without a visible error, it might be OOM-killed due to RAM overflowing the specifiedlimit. - If your resources are limited, you may try setting up
"--max-model-len=8192"underbaseArgsand explore other options to optimize the model. - Model slugs must be valid DNS-1035 labels: lowercase alphanumeric and hyphens only, starting with a letter. Dots and underscores are not allowed (e.g. use
qwen3-0-6b, notqwen3-0.6b).
Step 4 — Access the stack
Once the pods are running, the stack is reachable in two ways.
Gateway API (OpenAI-compatible endpoint)
The inference gateway exposes an OpenAI-compatible API. Use the LoadBalancer IP or internal service address to send requests:
curl http://<gateway-ip>/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen3.6-27B",
"messages": [{"role": "user", "content": "Hello!"}]
}'For local clusters without a LoadBalancer, use port-forward:
kubectl port-forward svc/inference-gateway 8080:80 -n thalamusOpen WebUI
thalamus includes Open WebUI, a browser-based chat interface. It is reachable via the hostname configured in your open-webui.route.hostnames value, or via port-forward for local access:
kubectl port-forward svc/open-webui 8080:80 -n open-webuiThen open http://localhost:8080 in your browser.
Local development (CPU-only)
For a lightweight local setup without a GPU, use helm/example.values.cpu.yaml as your values file. The modest resource requests make it suitable for local clusters like minikube or kind.
helmfile --file helm/helmfile.yaml.gotmpl apply \
--state-values-file helm/example.values.cpu.yamlNote: The CPU image has no Apple Silicon / Metal acceleration. Inference will be significantly slower than on a GPU or native macOS runtimes like Ollama. Note: When using the Docker driver (default on macOS), Docker does not fully virtualize memory — vLLM sees the entire host RAM and will attempt to allocate a large fraction of it, exceeding your container limits and causing an OOM kill. Set
--gpu-memory-utilizationexplicitly to avoid this.
Next Steps
- Browse the Model CRD API Reference for all available fields.
- Read about the planned architecture.