onyx Documentation

Open Source AI Platform - AI Chat with advanced features that works with every LLM

RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy onyx on any server, homelab, or cloud container platform.

File: README.md

[ ](https://www.onyx.app/?utm_source=onyx_repo&utm_medium=github&utm_campaign=readme)

Onyx - The Open Source AI Platform

Onyx is the application layer for LLMs - bringing a feature-rich interface that can be easily hosted by anyone.
Onyx enables LLMs through advanced capabilities like RAG, web search, code execution, file creation, deep research and more.

Connect your applications with over 50+ indexing based connectors provided out of the box or via MCP.

[!TIP]
Deploy with a single command:

text
curl -fsSL https://onyx.app/install_onyx.sh | bash

โญ Features

  • ๐Ÿ” Agentic RAG: Get best in class search and answer quality based on hybrid index + AI Agents for information retrieval
    • Benchmark to release soon!
  • ๐Ÿ”ฌ Deep Research: Get in depth reports with a multi-step research flow.
  • ๐Ÿค– Custom Agents: Build AI Agents with unique instructions, knowledge, and actions.
  • ๐ŸŒ Web Search: Browse the web to get up to date information.
    • Supports Serper, Google PSE, Brave, SearXNG, and others.
    • Comes with an in house web crawler and support for Firecrawl/Exa.
  • ๐Ÿ“„ Artifacts: Generate documents, graphics, and other downloadable artifacts.
  • โ–ถ๏ธ Actions & MCP: Let Onyx agents interact with external applications, comes with flexible Auth options.
  • ๐Ÿ’ป Code Execution: Execute code in a sandbox to analyze data, render graphs, or modify files.
  • ๐ŸŽ™๏ธ Voice Mode: Chat with Onyx via text-to-speech and speech-to-text.
  • ๐ŸŽจ Image Generation: Generate images based on user prompts.

Onyx supports all major LLM providers, both self-hosted (like Ollama, LiteLLM, vLLM, etc.) and proprietary (like Anthropic, OpenAI, Gemini, etc.).

To learn more - check out our docs!


๐Ÿš€ Deployment Modes

Onyx supports deployments in Docker, Kubernetes, Helm/Terraform and provides guides for major cloud providers.
Detailed deployment guides found here.

Onyx supports two separate deployment options: standard and lite.

Onyx Lite

The Lite mode can be thought of as a lightweight Chat UI. It requires less resources (under 1GB memory) and runs a less complex stack.
It is great for users who want to test out Onyx quickly or for teams who are only interested in the Chat UI and Agents functionalities.

Standard Onyx

The complete feature set of Onyx which is recommended for serious users and larger teams. Additional components not included in Lite mode:

  • Vector + Keyword index for RAG.
  • Background containers to run job queues and workers for syncing knowledge from connectors.
  • AI model inference servers to run deep learning models used during indexing and inference.
  • Performance optimizations for large scale use via in memory cache (Redis) and blob store (MinIO).

[!TIP]
To try Onyx for free without deploying, visit Onyx Cloud.


๐Ÿข Onyx for Enterprise

Onyx is built for teams of all sizes, from individual users to the largest global enterprises:

  • ๐Ÿ‘ฅ Collaboration: Share chats and agents with other members of your organization.
  • ๐Ÿ” Single Sign On: SSO via Google OAuth, OIDC, or SAML. Group syncing and user provisioning via SCIM.
  • ๐Ÿ›ก๏ธ Role Based Access Control: RBAC for sensitive resources like access to agents, actions, etc.
  • ๐Ÿ“Š Analytics: Usage graphs broken down by teams, LLMs, or agents.
  • ๐Ÿ•ต๏ธ Query History: Audit usage to ensure safe adoption of AI in your organization.
  • ๐Ÿ’ป Custom code: Run custom code to remove PII, reject sensitive queries, or to run custom analysis.
  • ๐ŸŽจ Whitelabeling: Customize the look and feel of Onyx with custom naming, icons, banners, and more.

๐Ÿ“š Licensing

There are two editions of Onyx:

  • Onyx Community Edition (CE) is available freely under the MIT license and covers all of the core features for Chat, RAG, Agents, and Actions.
  • Onyx Enterprise Edition (EE) includes extra features that are primarily useful for larger organizations.

For feature details, check out our website.

๐Ÿ‘ช Community

Join our open source community on Discord!

๐Ÿ’ก Contributing

Looking to contribute? Please check out the Contribution Guide for more details.


File: backend/alembic/README.md

Alembic DB Migrations

These files are for creating/updating the tables in the Relational DB (Postgres).
Onyx migrations use a generic single-database configuration with an async dbapi.

To generate new migrations:

From onyx/backend, run:
alembic revision -m <DESCRIPTION_OF_MIGRATION>

Note: you cannot use the --autogenerate flag as the automatic schema parsing does not work.

Manually populate the upgrade and downgrade in your new migration.

More info can be found here: https://alembic.sqlalchemy.org/en/latest/autogenerate.html

Running migrations

To run all un-applied migrations:
alembic upgrade head

To undo migrations:
alembic downgrade -X
where X is the number of migrations you want to undo from the current state

Multi-tenant migrations

For multi-tenant deployments, you can use additional options:

Upgrade all tenants:

bash
alembic -x upgrade_all_tenants=true upgrade head

Upgrade specific schemas:

bash
# Single schema
alembic -x schemas=tenant_12345678-1234-1234-1234-123456789012 upgrade head

# Multiple schemas (comma-separated)
alembic -x schemas=tenant_12345678-1234-1234-1234-123456789012,public,another_tenant upgrade head

Upgrade tenants within an alphabetical range:

bash
# Upgrade tenants 100-200 when sorted alphabetically (positions 100 to 200)
alembic -x upgrade_all_tenants=true -x tenant_range_start=100 -x tenant_range_end=200 upgrade head

# Upgrade tenants starting from position 1000 alphabetically
alembic -x upgrade_all_tenants=true -x tenant_range_start=1000 upgrade head

# Upgrade first 500 tenants alphabetically
alembic -x upgrade_all_tenants=true -x tenant_range_end=500 upgrade head

Continue on error (for batch operations):

bash
alembic -x upgrade_all_tenants=true -x continue=true upgrade head

The tenant range filtering works by:

  1. Sorting tenant IDs alphabetically
  2. Using 1-based position numbers (1st, 2nd, 3rd tenant, etc.)
  3. Filtering to the specified range of positions
  4. Non-tenant schemas (like 'public') are always included

File: backend/alembic_tenants/README.md

These files are for public table migrations when operating with multi tenancy.

If you are not a Onyx developer, you can ignore this directory entirely.


File: backend/generated/README.md

  • Generated Files
  • Generated files live here. This directory should be git ignored.

File: backend/onyx/connectors/README.md

Writing a new Onyx Connector

This README covers how to contribute a new Connector for Onyx. It includes an overview of the design, interfaces,
and required changes.

Thank you for your contribution!

Connector Overview

Connectors come in 3 different flows:

  • Load Connector:
    • Bulk indexes documents to reflect a point in time. This type of connector generally works by either pulling all
      documents via a connector's API or loads the documents from some sort of a dump file.
  • Poll Connector:
    • Incrementally updates documents based on a provided time range. It is used by the background job to pull the latest
      changes and additions since the last round of polling. This connector helps keep the document index up to date
      without needing to fetch/embed/index every document which would be too slow to do frequently on large sets of
      documents.
  • Slim Connector:
    • This connector should be a lighter weight method of checking all documents in the source to see if they still exist.
    • This connector should be identical to the Poll or Load Connector except that it only fetches the IDs of the documents, not the documents themselves.
    • This is used by our pruning job which removes old documents from the index.
    • The optional start and end datetimes can be ignored.
  • Event Based connectors:
    • Connectors that listen to events and update documents accordingly.
    • Currently not used by the background job, this exists for future design purposes.

Connector Implementation

Refer to interfaces.py
and this first contributor created Pull Request for a new connector (Shoutout to Dan Brown):
Reference Pull Request

For implementing a Slim Connector, refer to the comments in this PR:
Slim Connector PR

All new connectors should have tests added to the backend/tests/daily/connectors directory. Refer to the above PR for an example of adding tests for a new connector.

Implementing the new Connector

The connector must subclass one or more of LoadConnector, PollConnector, CheckpointedConnector, or CheckpointedConnectorWithPermSync

The __init__ should take arguments for configuring what documents the connector will and where it finds those
documents. For example, if you have a wiki site, it may include the configuration for the team, topic, folder, etc. of
the documents to fetch. It may also include the base domain of the wiki. Alternatively, if all the access information
of the connector is stored in the credential/token, then there may be no required arguments.

load_credentials should take a dictionary which provides all the access information that the connector might need.
For example this could be the user's username and access token.

Refer to the existing connectors for load_from_state and poll_source examples. There is not yet a process to listen
for EventConnector events, this will come down the line.

Development Tip

It may be handy to test your new connector separate from the rest of the stack while developing.
Follow the below template:

commandline
if __name__ == "__main__":
    import time
    test_connector = NewConnector(space="engineering")
    test_connector.load_credentials({
        "user_id": "foobar",
        "access_token": "fake_token"
    })
    all_docs = test_connector.load_from_state()

    current = time.time()
    one_day_ago = current - 24 * 60 * 60  # 1 day
    latest_docs = test_connector.poll_source(one_day_ago, current)

Note: Be sure to set PYTHONPATH to onyx/backend before running the above main.

Supporting "Include Attachments"

If the source has attachments (files attached to pages, tickets, etc.), let admins opt out of
indexing them by following the shared convention:

  • Accept an include_attachments: bool kwarg in the connector's __init__. The value flows in
    automatically from connector_specific_config; no factory or API changes are needed.
    • New connectors should default it to False.
    • When retrofitting a connector that already indexes attachments unconditionally, default it to
      True โ€” existing connector rows have no include_attachments key stored, so the constructor
      default is what they get and their behavior must not change.
  • Gate every attachment enumeration path on the flag: the main indexing pass and the
    slim-doc pass (pruning / permission sync) if the connector has one. The slim pass must admit
    exactly the same documents as the main pass โ€” emitting attachment slim docs the main pass
    skipped leaves permanent chunk_count IS NULL rows, while correctly omitting them lets pruning
    clean up previously indexed attachments after an admin turns the setting off.
  • In the frontend form, add buildIncludeAttachmentsOption(<default>) from
    web/src/lib/connectors/connectors.tsx to the connector's connectorConfigs entry (use the
    same default as the backend), and add include_attachments?: boolean to the connector's config
    interface in that file.

ConfluenceConnector and DrupalWikiConnector are reference implementations.

Additional Required Changes:

Backend Changes

  • Add a new type to
    DocumentSource
  • Add a mapping from DocumentSource (and optionally connector type) to the right connector class
    here

Frontend Changes

  • Add the new Connector definition to the SOURCE_METADATA_MAP here.
  • Add the definition for the new Form to the connectorConfigs object here.

Docs Changes

Create the new connector page (with guiding images!) with how to get the connector credentials and how to set up the
connector in Onyx. Then create a Pull Request in https://github.com/onyx-dot-app/documentation.

Before opening PR

  1. Be sure to fully test changes end to end with setting up the connector and updating the index with new docs from the
    new connector. To make it easier to review, please attach a video showing the successful creation of the connector via the UI (starting from the Add Connector page).
  2. Add a folder + tests under backend/tests/daily/connectors director. For an example, checkout the test for Confluence. In the PR description, include a guide on how to setup the new source to pass the test. Before merging, we will re-create the environment and make sure the test(s) pass.
  3. Be sure to run the linting/formatting, refer to the formatting and linting section in
    CONTRIBUTING.md

File: backend/slackbot_images/README.md

This folder is kept for backwards compatibility with released Slackbot versions that
hardcode GitHub raw URLs under backend/slackbot_images.

New Slackbot source icons live in web/public/slackbot-source-icons.

TODO(wenxi-onyx): Remove this directory after 2026-07-24, once stable releases have
moved off these legacy GitHub raw URLs.


File: deployment/aws_ecs_fargate/cloudformation/README.md

Onyx AWS ECS Fargate CloudFormation Deployment

This directory contains CloudFormation templates and scripts to deploy Onyx on AWS ECS Fargate.

Configuration

All configuration parameters are stored in a single JSON file: onyx_config.json. This file contains all the parameters needed for the different CloudFormation stacks.

Example:

json
{
  "OnyxNamespace": "onyx",
  "Environment": "production",
  "EFSName": "onyx-efs",
  "AWSRegion": "us-east-2",
  "VpcID": "YOUR_VPC_ID",
  "SubnetIDs": "YOUR_SUBNET_ID1,YOUR_SUBNET_ID2",
  "DomainName": "YOUR_DOMAIN e.g ecs.onyx.app",
  "ValidationMethod": "DNS",
  "HostedZoneId": ""
}

Required Parameters

  • Environment: Used to prefix all stack names during deployment. This is required.
  • OnyxNamespace: Namespace for the Onyx deployment.
  • EFSName: Name for the Elastic File System.
  • AWSRegion: AWS region where resources will be deployed.
  • VpcID: ID of the VPC where Onyx will be deployed.
  • SubnetIDs: Comma-separated list of subnet IDs for deployment.
  • DomainName: Domain name for the Onyx deployment.
  • ValidationMethod: Method for domain validation (typically "DNS").
  • [optional] HostedZoneId: Route 53 hosted zone ID (only if using Route 53 for DNS).

The deployment script automatically extracts the needed parameters for each CloudFormation template based on the parameter names defined in the templates.

Deployment Order

The deployment follows this order:

  1. Infrastructure stacks:

    • EFS
    • Cluster
    • ACM
  2. Service stacks:

    • Postgres
    • Redis
    • Vespa Engine
    • Model Server (Indexing)
    • Model Server (Inference)
    • Backend API Server
    • Backend Background Server
    • Web Server
    • Nginx

Usage

To deploy:

bash
./deploy.sh

To uninstall:

bash
./uninstall.sh

File: deployment/docker_compose/README.md

Welcome to Onyx

To set up Onyx there are several options, Onyx supports the following for deployment:

  1. Quick guided install via the install.sh script
  2. Pulling the repo and running docker compose up -d from the deployment/docker_compose directory
  • Note, it is recommended to copy over the env.template file to .env and edit the necessary values
  1. For large scale deployments leveraging Kubernetes, there are two options, Helm or Terraform.

This README focuses on the easiest guided deployment which is via install.sh.

For more detailed guides, please refer to the documentation: https://docs.onyx.app/deployment/overview

install.sh script

text
curl -fsSL https://raw.githubusercontent.com/onyx-dot-app/onyx/main/deployment/docker_compose/install.sh > install.sh && chmod +x install.sh && ./install.sh

The script installs the Onyx CLI (onyx-cli) and hands over to onyx-cli deploy install, which is
where the guided installation lives. Any flags you pass are forwarded to it. If you already have the
CLI (pip install onyx-cli), skip the script and run onyx-cli deploy install directly.

This provides a guided installation of Onyx via Docker Compose. It will deploy the latest version of Onyx
and set up the volumes to ensure data is persisted across deployments or upgrades.

The deployment files are stored in ~/.config/onyx (an existing onyx_data directory from an older
install is detected and kept in place; --dir targets another location). Note that no application
critical data is stored in that directory so even if you delete it, the data needed to restore the app
will not be destroyed.

The data about chats, users, etc. are instead stored as named Docker Volumes. This is managed by Docker
and where it is stored will depend on your Docker setup. You can always delete these as well by running
onyx-cli deploy uninstall.

To shut down the deployment without deleting, use onyx-cli deploy stop.

Managing the deployment

Beyond installing, the CLI covers the rest of the lifecycle:

Command What it does
onyx-cli deploy status Installed version, containers, and health (--json for scripts)
onyx-cli deploy logs [service...] Logs of the deployment's containers
onyx-cli deploy stop Stop the containers, keep the data
onyx-cli deploy upgrade [--tag vX.Y.Z] Upgrade in place (see below)
onyx-cli deploy uninstall Remove the containers, volumes, and deployment directory

Upgrading the deployment

Onyx maintains backwards compatibility across all minor versions following SemVer, so upgrading is
onyx-cli deploy upgrade (add --tag vX.Y.Z to pin a version). It rewrites only IMAGE_TAG, preserves
your .env edits, and backs up hand-edited files.

If you are more comfortable running docker compose commands, you can also run commands directly from
the directory with the docker-compose.yml file. First bring the containers down (docker compose down),
verify the version you want in the environment file (see below), (if using latest tag, be sure to run
docker compose pull) and run docker compose up to restart the services on the latest version

Environment variables

The Docker Compose files try to look for a .env file in the same directory. The installer sets it up
from a file called env.template. Feel free to edit the .env file to customize your deployment. The most
important / common changed values are located near the top of the file. Later onyx-cli deploy runs
keep your edits.

IMAGE_TAG is the version of Onyx to run. It is recommended to leave it as latest to get all updates with each redeployment.

Every image publishes a -dev twin for each of its tags (e.g. latest-dev, v1.2.3-dev), so a single
IMAGE_TAG=latest-dev selects the dev variant of the whole deployment. Today only the backend image actually differs:
its -dev twin adds interactive debugging tools (vim, nano, curl, ps, psql) that the default image leaves out to stay
minimal. The web-server, model-server, and sandbox -dev tags are identical to their plain counterparts and exist so
that one version string covers every image.


File: deployment/helm/README.md

Installing the chart

Helm repository (default)

bash
helm repo add onyx https://onyx-dot-app.github.io/onyx
helm repo update
helm install onyx onyx/onyx -n onyx --create-namespace

Chart tarballs are attached to GitHub releases named helm/onyx-<version>, and
index.yaml on the gh-pages branch points at them. This is an implementation
detail: the repository URL above does not change and helm repo add,
helm search repo and helm upgrade all work as before.

OCI registry (alternative)

The same chart is published to GHCR:

bash
helm install onyx oci://ghcr.io/onyx-dot-app/charts/onyx \
  --version 0.8.16 -n onyx --create-namespace

helm repo add does not accept oci:// URLs, so an OCI install needs an
explicit --version. There is no helm repo update or helm search repo for
OCI and no helm command that lists the available versions. Read them from the
helm/onyx-<version> releases
or from the Helm repository index:

bash
helm search repo onyx/onyx --versions   # needs `helm repo add` from above

To inspect one version before installing it:

bash
helm show chart oci://ghcr.io/onyx-dot-app/charts/onyx --version 0.8.16

Both channels ship identical bytes from the same build. Use the Helm repository
unless you specifically need OCI.

Recent chart changes (0.5.0)

If you are upgrading from an earlier 0.4.x release, read MIGRATION.md first. The 0.5.0 release dropped the bundled Vespa subchart; chart 0.5.6 ships a guard that fails helm upgrade if the legacy da-vespa StatefulSet is still in the namespace so you don't lose the indexed data silently.

Other 0.5.0 changes:

  • Redis โ€” the chart now bundles the redis-operator subchart alongside the
    redis subchart. The operator installs the CRD that the Redis CR binds to,
    so helm install works on a clean cluster without a separate CRD step.
  • PostgreSQL โ€” a CloudNativePG Cluster CR is now rendered from
    templates/postgres-cluster.yaml, so helm install provisions postgres
    out of the box. The Cluster is named <release>-pg (service:
    <release>-pg-rw). Set postgresql.enabled: false and override
    configMap.POSTGRES_HOST if you use an external database (RDS, etc.).
  • REDIS_HOST โ€” the configmap no longer appends -master to the redis
    service name. If you previously overrode REDIS_HOST or had clients that
    hard-coded the -master suffix, update them to point at the
    redis-operator standalone service.
  • CNPG CRDs โ€” copied into crds/cnpg-crds.yaml so Helm pre-installs
    them before templates. The subchart's own CRD template is disabled
    (postgresql.crds.create: false). Run scripts/check-cnpg-crds.sh after
    bumping the CNPG subchart version to verify the copy is in sync.
  • Pre-delete hook โ€” a cleanup Job (templates/pre-delete-cleanup.yaml)
    deletes operator-managed CRs before helm uninstall tears down the
    operators, ensuring finalizers are processed and namespace cleanup
    completes promptly.

Dependency updates (when subchart versions are bumped)

  • If updating subcharts, you need to run this before committing!
  • cd charts/onyx
  • helm dependency update .

Prerequisites

Redis operator (automatic with bundled Redis)

When redis.enabled: true (the chart default), the chart bundles and automatically
installs the redis-operator subchart alongside the redis subchart. The operator
provides the CRD that the Redis CR binds to, so helm install works on a clean
cluster without manual preparation.

No separate pre-install step is needed โ€” the chart handles the redis-operator
CRD and controller automatically as part of helm install onyx onyx/onyx.

If you don't want the bundled Redis (recommended for production environments using
managed Redis like AWS ElastiCache), see Using an external Redis
below and set redis.enabled: false to skip the operator entirely.

Onyx Craft with Kubernetes sandboxes

When configMap.ENABLE_CRAFT="true", the target cluster must run Kubernetes
>= 1.33. Craft Helm deployments provision Kubernetes sandbox pods; Docker
is not a Helm deployment backend for Craft and configMap.SANDBOX_BACKEND is
not a bypass for this requirement.

Craft sandbox pods use native restartable init sidecar containers:
sandbox-init runs and completes first, sidecar starts next as an
initContainers entry with restartPolicy: Always, and the main sandbox app
container starts after the sidecar's startup restore gate is ready.

The chart fails during render/install on older clusters so the incompatibility
is caught before sandbox provisioning. This guard does not apply to non-Craft
installs.

Using an external Redis

To point Onyx at an externally-managed Redis (e.g. AWS ElastiCache) and skip
the bundled Redis + operator entirely:

yaml
redis:
  enabled: false

configMap:
  # Override the chart-computed REDIS_HOST. Use the cluster/primary endpoint
  # from your managed Redis. Avoid underscores โ€” they are invalid in K8s DNS
  # labels and look identical to a hung service.
  REDIS_HOST: "<elasticache-primary-endpoint>"
  REDIS_PORT: "6379"
  REDIS_SSL: "true"   # set to "true" if in-transit encryption is enabled

auth:
  redis:
    enabled: true
    # Pre-create a Secret in the release namespace holding the Redis password
    # under the key `redis_password`.
    existingSecret: "elasticache-auth-secret"
    secretKeys:
      REDIS_PASSWORD: redis_password

Sourcing secrets from an external secret manager (ESO)

The chart can render an ExternalSecret CR that has External Secrets
Operator
materialize a single Kubernetes Secret
from an upstream provider (AWS Secrets Manager, GCP Secret Manager, Vault,
etc.). All auth.* sections then read their per-section keys from that
materialized Secret, and any loose env-var secrets are projected into pods
via envFrom.

Prerequisite: the chart does not install ESO. Install it separately and
create a SecretStore or ClusterSecretStore (e.g. aws-secrets-manager)
before enabling externalSecret. See https://external-secrets.io/ for setup.

The upstream secret should be a JSON object whose top-level keys map 1:1 to
the keys the chart expects in the materialized Secret (the union of every
auth.<section>.secretKeys value plus any loose env-var names you project via
extraEnvFromSecret).

yaml
externalSecret:
  enabled: true
  # Identifier of the upstream secret (e.g. AWS Secrets Manager secret name).
  refPath: "onyx/<customer>/app-secrets"
  # Name of the materialized k8s Secret. Point auth.*.existingSecret here.
  secretName: onyx-app-secrets
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  refreshInterval: 1h

# Inject every key from onyx-app-secrets as an env var on backend pods.
# Use for secrets that today live as plaintext in `configMap:` blocks.
extraEnvFromSecret: onyx-app-secrets

auth:
  postgresql:
    existingSecret: onyx-app-secrets
    secretKeys:
      POSTGRES_USER: postgres_user
      POSTGRES_PASSWORD: postgres_password
  redis:
    existingSecret: onyx-app-secrets
    secretKeys:
      REDIS_PASSWORD: redis_password
  # ...repeat for any other auth section that should read from the shared Secret.

When externalSecret.enabled: false (the default) the chart renders exactly
as before โ€” no ExternalSecret CR is created and envFrom is unchanged.

First-install reconciliation window

The Helm release renders both the ExternalSecret CR and the Deployments that
reference the materialized Secret in the same apply. ESO reconciles the CR
asynchronously, typically within a few seconds. During that brief window,
backend pods will be in CreateContainerConfigError because their
envFrom: secretRef references a Secret that does not yet exist. The kubelet
retries pod creation automatically; once ESO materializes the Secret, pods
transition to Running without manual intervention.

We intentionally leave the secretRef non-optional. Marking it optional would
allow pods to boot with empty env vars and fail later in a way that is harder
to diagnose; the brief CreateContainerConfigError is the clearer failure
mode if anything is misconfigured (wrong SM path, missing IRSA permissions,
missing keys in the upstream blob).

If you need pods to be Ready before traffic is routed, rely on the existing
readiness probes โ€” they will not pass until the env vars are present.

Model-server pods

indexing-model-deployment and inference-model-deployment deliberately do
not consume extraEnvFromSecret. Model servers only need model-config
env vars (already in the chart's configMap), so injecting application-level
secrets like POSTGRES_PASSWORD would needlessly widen their secret-exposure
surface.

Values that come from docker-compose (do not copy them)

The Onyx docker-compose stack uses service-name hostnames like api_server,
inference_model_server, and cache. Those names contain underscores, which
are invalid in Kubernetes DNS labels โ€” DNS lookups for them will fail and
the symptom is often a blank login page or TypeError: fetch failed in web
pod logs.

The chart computes correct K8s service hostnames for you. If you are
adapting an .env file from docker-compose, remove these keys from your
Helm configMap: and let the chart fill them in:

Key Don't set to (docker-compose) Chart-computed default
REDIS_HOST cache `redis.redisStandalone.name
INTERNAL_URL http://api_server:8080 http://<release>-api-service:8080
API_SERVER_HOST api_server computed
MODEL_SERVER_HOST inference_model_server <release>-inference-model-service
INDEXING_MODEL_SERVER_HOST indexing_model_server <release>-indexing-model-service

Other docker-compose-style values you should set deliberately:

  • DOMAIN should be your public hostname (e.g. onyx.example.com), not
    localhost. It affects cookies and CORS.
  • WEB_DOMAIN should be the full origin (e.g. https://onyx.example.com).
    Watch for typos like hhttps://...; they silently break email links and
    OAuth redirects.

Local testing

This section covers chart-maintainer testing; for the Onyx Craft local-kind developer workflow, see docs/craft/dev/local-kubernetes.md.

One time setup

  • brew install kind
  • Ensure you have no config at ~/.kube/config
  • kind create cluster
  • mv ~/.kube/config ~/.kube/kind-config

Automated install and test with ct

  • export KUBECONFIG=~/.kube/kind-config
  • kubectl config use-context kind-kind
  • from source root run the following. This does a very basic test against the web server
    • ct install --all --helm-extra-set-args="--set=nginx.enabled=false" --debug --config ct.yaml

Output template to file and inspect

  • cd charts/onyx
  • helm template test-output . --set auth.opensearch.values.opensearch_admin_password='StrongPassword123!' > test-output.yaml
  • Craft Kubernetes sandbox version guard check:
    • expect failure:
      helm template test-output . -f values-ci.yaml --kube-version 1.32.0 --show-only templates/craft-kubernetes-version-check.yaml
    • expect success:
      helm template test-output . -f values-ci.yaml --kube-version 1.33.0 --show-only templates/craft-kubernetes-version-check.yaml

Test the entire cluster manually

  • cd charts/onyx
  • helm install onyx . -n onyx --set postgresql.primary.persistence.enabled=false --set auth.opensearch.values.opensearch_admin_password='StrongPassword123!'
    • the postgres flag is to keep the storage ephemeral for testing. You probably don't want to set that in prod.
    • the OpenSearch admin password must be set on first install unless you are supplying auth.opensearch.existingSecret.
    • no flag for ephemeral vespa storage yet, might be good for testing
  • kubectl -n onyx port-forward service/onyx-nginx 8080:80
    • this will forward the local port 8080 to the installed chart for you to run tests, etc.
  • When you are finished
    • helm uninstall onyx -n onyx
    • Vespa leaves behind a PVC. Delete it if you are completely done.
      • k -n onyx get pvc
      • k -n onyx delete pvc vespa-storage-da-vespa-0
    • If you didn't disable Postgres persistence earlier, you may want to delete that PVC too.

Run as non-root user

By default, some onyx containers run as root. If you'd like to explicitly run the onyx containers as a non-root user, update the values.yaml file for the following components:

  • celery_shared, api, webserver, indexCapability, inferenceCapability
    yaml
    securityContext:
      runAsNonRoot: true
      runAsUser: 1001
  • vespa
    yaml
    podSecurityContext:
      fsGroup: 1000
    securityContext:
      privileged: false
      runAsUser: 1000

Resourcing

In the helm charts, we have resource suggestions for all Onyx-owned components.
These are simply initial suggestions, and may need to be tuned for your specific use case.

Please talk to us in Slack if you have any questions!

Autoscaling options

The chart renders Kubernetes HorizontalPodAutoscalers by default. To keep this behavior, leave
autoscaling.engine as hpa and adjust the per-component autoscaling.* values as needed.

If you would like to use KEDA ScaledObjects instead:

  1. Install and manage the KEDA operator in your cluster yourself (for example via the official KEDA Helm chart). KEDA is no longer packaged as a dependency of the Onyx chart.
  2. Set autoscaling.engine: keda in your values.yaml and enable autoscaling for the components you want to scale.

When autoscaling.engine is set to keda, the chart will render the existing ScaledObject templates; otherwise HPAs will be rendered.


File: deployment/README.md

Documentation for how to deploy Onyx can be found in our official docs:
https://docs.onyx.app/deployment/overview

Maintaining the compose files (contributors)

docker_compose/docker-compose.yml, docker-compose.prod.yml and
docker-compose.prod-no-letsencrypt.yml are generated from the single source of truth
docker_compose/docker-compose.template.yml โ€” do not hand-edit them. To change any of the three,
edit the template (per-variant differences are expressed with #!for / #!only / #!value
directives, documented in ods generate-compose --help) and regenerate:

text
ods generate-compose --write

The generator lives in tools/ods (internal/composegen) and ships with the onyx-devtools
package. The docker-compose-sync pre-commit hook runs it automatically for commits touching the
template or the generated files, so a stray edit to a generated file gets reverted on the next
commit.

onyx-cli embeds copies of the guided-install deployment files (the generated
docker-compose.yml and docker-compose.prod.yml, the lite/craft overlays, the env templates,
the nginx config, and docker_compose/README.md) under
cli/internal/deploy/deployfiles/embedded/. The same ods generate-compose --write run refreshes
them after rendering the variants, and a drift test in the cli module (go test ./...) gates
staleness. If you change any of those files, re-run the generator and commit the refreshed embedded
copies.

Note that docker_compose/README.md ships inside every self-hosted install, so keep it
user-facing โ€” contributor notes like this one belong here instead.