Overview
The backend is a Node.js REST API that handles authentication, business logic, data-connector queries, and LLM orchestration. It runs on port 3000 and is exposed through Gateway API or Ingress, usually on its own API hostname.
Set CONNECT_SERVICE_URL to the in-cluster Connect service URL when connect.enabled: true, for example http://cobi-dashboard-connect:8000.
Required Secrets
Create the backend Kubernetes Secret before running helm install. The backend will crash-loop if the Secret is missing or incomplete.
kubectl create secret generic cobi-backend-secrets \
--namespace cobi \
# ── Database ─────────────────────────────────────────────────────────
--from-literal=DATABASE_URL="postgres://postgres:<password>@<pg-host>:5432/dashboard_auth?schema=public" \
--from-literal=CORE_DATABASE_URL="postgres://postgres:<password>@<pg-host>:5432/cobi_core?schema=public" \
--from-literal=ENABLE_MIGRATIONS="true" \
# ── Auth ─────────────────────────────────────────────────────────────
--from-literal=BETTER_AUTH_SECRET="<random-64-char-string>" \
--from-literal=BETTER_AUTH_URL="https://api.example.com" \
--from-literal=FRONTEND_URL="https://app.example.com" \
--from-literal=CONNECT_SERVICE_URL="http://cobi-dashboard-connect:8000" \
# ── LLM inference ────────────────────────────────────────────────────
--from-literal=AI_PROVIDER="openai" \
--from-literal=OPENAI_API_KEY="not-used" \
--from-literal=OPENAI_BASE_URL="http://cobi-dashboard-vllmstack-engine:8000/v1" \
--from-literal=DEFAULT_MODEL_ID="QuantTrio/Qwen3.5-9B-AWQ" \
--from-literal=DEFAULT_FAST_MODEL_ID="QuantTrio/Qwen3.5-9B-AWQ" \
--from-literal=DEFAULT_OCR_MODEL_ID="QuantTrio/Qwen3.5-9B-AWQ" \
# ── Vector store (Qdrant) ────────────────────────────────────────────
--from-literal=QDRANT_URL="http://cobi-dashboard-qdrant:6333" \
--from-literal=QDRANT_LOCAL_ONLY="true" \
--from-literal=QDRANT_API_KEY="" \
--from-literal=QDRANT_CLUSTER_KEY="" \
--from-literal=QDRANT_COLLECTION_PREFIX="prod_" \
# ── Object storage (MinIO or AWS S3) ─────────────────────────────────
--from-literal=S3_BUCKET="documents" \
--from-literal=S3_REGION="us-east-1" \
--from-literal=S3_ENDPOINT="http://cobi-dashboard-minio:9000" \
--from-literal=S3_ACCESS_KEY_ID="admin" \
--from-literal=S3_SECRET_ACCESS_KEY="<minio-root-password>" \
# ── Security ─────────────────────────────────────────────────────────
--from-literal=DATASOURCES_ENCRYPTION_KEY="<random-64-hex-chars>"
After the first successful deployment, change ENABLE_MIGRATIONS to "false" and re-apply the Secret. Running migrations on every pod restart is not safe in production.
Environment Variable Reference
Database
| Variable | Required | Description |
|---|
DATABASE_URL | Yes | PostgreSQL connection string for the dashboard_auth database (auth, sessions) |
CORE_DATABASE_URL | Yes | PostgreSQL connection string for the cobi_core database (application data) |
ENABLE_MIGRATIONS | Yes | Set "true" on first install to run schema migrations; "false" after |
Both connection strings follow the format:
postgres://<user>:<password>@<host>:<port>/<database>?schema=public
For the in-cluster PostgreSQL subchart, the host is <release-name>-postgresql (e.g. cobi-dashboard-postgresql).
Authentication
| Variable | Required | Description |
|---|
BETTER_AUTH_SECRET | Yes | Secret key for signing session tokens. Generate with openssl rand -hex 32 |
BETTER_AUTH_URL | Yes | Public HTTPS URL of the application — used for auth callbacks and CSRF checks |
FRONTEND_URL | Yes | Public HTTPS URL of the frontend — used for redirect allowlists |
BETTER_AUTH_URL must match the public API URL. FRONTEND_URL must match the public frontend URL.
LLM Inference
| Variable | Required | Description |
|---|
AI_PROVIDER | Yes | Always "openai" — the backend uses the OpenAI-compatible API |
OPENAI_BASE_URL | Yes | Base URL of the vLLM inference service (e.g. http://cobi-dashboard-vllmstack-engine:8000/v1) |
OPENAI_API_KEY | Yes | API key. Set to any non-empty string for self-hosted vLLM (it ignores the key by default) |
DEFAULT_MODEL_ID | Yes | Model ID passed in inference requests (must match the model loaded in vLLM) |
DEFAULT_FAST_MODEL_ID | No | Model for fast/cheap requests. Defaults to DEFAULT_MODEL_ID if not set |
DEFAULT_OCR_MODEL_ID | No | Model for OCR document extraction. Defaults to DEFAULT_MODEL_ID if not set |
When using OpenRouter or another external provider instead of vLLM:
--from-literal=OPENAI_BASE_URL="https://openrouter.ai/api/v1" \
--from-literal=OPENAI_API_KEY="sk-or-..." \
--from-literal=DEFAULT_MODEL_ID="qwen/qwen3.5-9b"
Vector Store (Qdrant)
| Variable | Required | Description |
|---|
QDRANT_URL | Yes | HTTP URL of the Qdrant service (e.g. http://cobi-dashboard-qdrant:6333) |
QDRANT_LOCAL_ONLY | Yes | Set "true" for in-cluster or on-prem Qdrant; "false" for Qdrant Cloud |
QDRANT_COLLECTION_PREFIX | No | String prefix for all collection names. Useful to namespace multiple environments sharing one Qdrant instance (e.g. "prod_", "staging_") |
QDRANT_API_KEY | Conditional | Required when using Qdrant Cloud. Leave empty for local |
QDRANT_CLUSTER_KEY | Conditional | Required when using Qdrant Cloud. Leave empty for local |
Object Storage
| Variable | Required | Description |
|---|
S3_BUCKET | Yes | Bucket name for document storage |
S3_REGION | Yes | Signing region. Set to "us-east-1" for MinIO |
S3_ENDPOINT | Yes for S3-compatible storage | Custom S3 endpoint. Set to "http://cobi-dashboard-minio:9000" when using in-cluster MinIO |
S3_ACCESS_KEY_ID | Yes for static credentials | S3 access key ID, or MinIO rootUser |
S3_SECRET_ACCESS_KEY | Yes for static credentials | S3 secret access key, or MinIO rootPassword |
When backend.s3.existingSecret is set, the Helm chart maps these generic S3_* Secret keys to the backend container’s runtime environment variables.
Security
| Variable | Required | Description |
|---|
DATASOURCES_ENCRYPTION_KEY | Yes | 64-character hex key used to encrypt stored data-source credentials at rest. Generate with openssl rand -hex 32 |
Observability
| Variable | Required | Description |
|---|
OTEL_EXPORTER_OTLP_ENDPOINT | No | OTLP receiver URL (e.g. http://cobi-dashboard-otel-lgtm:4318). Set when otel-lgtm is enabled |
OTEL_SERVICE_NAME | No | Service name shown in traces (e.g. "cobi-backend") |
OTEL_SERVICE_VERSION | No | Service version shown in traces |
OTEL_METRIC_EXPORT_INTERVAL | No | Metric export interval in milliseconds (default 5000) |
POSTHOG_API_KEY | No | PostHog project API key for product analytics |
POSTHOG_HOST | No | PostHog ingest host (e.g. https://eu.i.posthog.com) |
POSTHOG_PERSONAL_API_KEY | No | PostHog personal API key for server-side calls |
LANGFUSE_PUBLIC_KEY | No | Langfuse project public key for LLM tracing |
LANGFUSE_SECRET_KEY | No | Langfuse project secret key |
LANGFUSE_HOST | No | Langfuse host (e.g. https://cloud.langfuse.com) |
LANGFUSE_TRACING_ENVIRONMENT | No | Environment label in Langfuse traces (e.g. "production") |
LANGFUSE_PROMPT_RUNTIME_MODE | No | "local-only" to load prompts from bundled files; "langfuse" to fetch from Langfuse at runtime |
LANGFUSE_PROMPT_LABEL | No | Prompt version label to load from Langfuse (default "latest") |
LANGFUSE_PROMPT_BUNDLE_PATH | No | File path of the bundled prompt file when LANGFUSE_PROMPT_RUNTIME_MODE=local-only |
Helm Values
Reference the Secret in your values file:
backend:
enabled: true
image:
repository: docker.io/hellocobi/dashboard-backend
tag: "0.2.6"
pullPolicy: IfNotPresent
replicaCount: 1
service:
type: ClusterIP
port: 3000
envFrom:
- secretRef:
name: cobi-backend-secrets
s3:
existingSecret: cobi-backend-secrets
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
Generating Secure Keys
# BETTER_AUTH_SECRET — 32-byte hex string
openssl rand -hex 32
# DATASOURCES_ENCRYPTION_KEY — 32-byte hex string (64 chars)
openssl rand -hex 32