This page describes Onyx Helm chart 0.8.32 or later. Earlier charts do not have the default readiness probes,
the preStop drain,
podDisruptionBudget, or topologySpreadConstraints described here.Replicas
These components are stateless. You can run more than one replica of each:apiandwebserverinferenceCapabilityandindexCapability(model servers)- The
celery_worker_*workers
celery_beat.replicaCount at 1. It is the task scheduler.
The bundled PostgreSQL, Redis, and OpenSearch each run as one pod by default. For highly available dependencies,
use managed services. See External Services.
Autoscaling
Each scalable component has anautoscaling block. The chart creates a HorizontalPodAutoscaler by default.
values.yaml
autoscaling.enabled is true, the chart ignores replicaCount.
KEDA
Setautoscaling.engine: keda to create KEDA ScaledObject resources instead.
You must install the KEDA operator yourself. Add KEDA triggers in customTriggers.
This example scales the document processing workers on queue depth.
It assumes that Prometheus scrapes the Onyx monitoring worker, which exports onyx_queue_depth.
Set monitoring.serviceMonitors.enabled: true if you use the Prometheus Operator.
values.yaml
Probes
For each of these components:
readinessProbe: {}uses the chart default.readinessProbe: nullremoves the probe.- A full probe spec replaces the default.
celery_shared.
Database migrations and probes
If you turn on a startup probe, makeperiodSeconds × failureThreshold longer than your longest migration:
values.yaml
helm upgrade --wait waits until the new API pods are ready, which is after the migrations complete.
Set --timeout longer than your longest migration. If you use --atomic,
a rollback reverts the Kubernetes objects but not the database schema.
To run migrations as a separate step, set api.runStartupMigrations: false.
Then run alembic upgrade head from the onyxdotapp/onyx-backend image, with the same configuration and Secrets,
before you roll out new API pods.
Graceful shutdown
The API server and web server sleep before they receiveSIGTERM.
This gives the ingress time to stop sending them new requests.
The API server then waits for in-flight chat streams until the grace period ends.
- The sleep counts against the grace period.
- The preStop sleep needs Kubernetes 1.30 or later. The chart leaves it out on older clusters.
- If your chats run longer than the grace period, increase
api.terminationGracePeriodSeconds.
Disruption budgets and spreading
PodDisruptionBudgets are off by default. SetpodDisruptionBudget.enabled: true on api, webserver,
inferenceCapability, indexCapability, or any celery_worker_* component.
- The default is
maxUnavailable: 1. You can setminAvailableinstead. - The chart creates the budget only when the component has more than one replica, or
autoscaling.minReplicasgreater than one. - The chart fails to render a budget that blocks every eviction.
topologySpreadConstraints is available on api, webserver, inferenceCapability, and indexCapability.
values.yaml
Long-running requests
Each chat stream holds one thread of the API threadpool until it ends. Deep research and code execution can hold a thread for many minutes. When all threads are busy, new requests wait in a queue.Readiness on a full threadpool
On Onyx v4.7.0 and later,GET /health/ready returns 503 when the threadpool has been full for 10 seconds.
The pod then leaves the Service until threads become free.
values.yaml
- Use
/health/readyonly with more than one replica. High load fills all replicas at the same time. With one replica, the Service has no ready pods. - Never use
/health/readyfor the liveness probe. It restarts pods during high load and causes an outage.
Stop a chat
Users stop a response with the stop button. API clients can sendPOST /api/chat/stop-chat-session/{chat_session_id}.
Onyx writes a stop signal to the cache, and the stream ends at its next check.
Any API replica can receive the stop request.
Limits
- Onyx has no admission control across replicas. Each pod limits only its own requests.
- Onyx has no durable queue for chat requests. A request runs on the pod that receives it. If that pod stops, the request stops.
api.limitConcurrency to reject excess requests quickly instead of queueing them.