Environment Variables
Complete reference for configuring a self-hosted Rhesis deployment. For a guided walkthrough of the essentials, see the Docker Compose guide.
Set variables in the .env.docker file you pass to Docker Compose:
docker compose --env-file .env.docker up -dEmpty values are treated as unset — the built-in default applies. In .env.example, commented lines show
the default each variable falls back to, so you only uncomment a line to override it.
Required
These four secrets have no default; the stack refuses to start if any is unset.
| Variable | Description | Generate with |
|---|---|---|
DB_ENCRYPTION_KEY | Encrypts sensitive fields (such as stored provider credentials) at rest | openssl rand -base64 32 | tr '+/' '-_' |
JWT_SECRET_KEY | Signs backend-issued JWTs | openssl rand -hex 32 |
NEXTAUTH_SECRET | Signs NextAuth (frontend) sessions | openssl rand -hex 32 |
SESSION_SECRET_KEY | Signs backend session cookies | openssl rand -hex 32 |
Public URLs and ports
| Variable | Description | Default |
|---|---|---|
FRONTEND_URL | Public origin of the frontend; an https:// value enables secure cookies | http://localhost:3000 |
API_BASE_URL | Public origin of the backend API; an https:// value enables secure cookies | http://localhost:8080 |
FRONTEND_PORT | Host port Docker publishes for the frontend | 3000 |
BACKEND_PORT | Host port Docker publishes for the backend | 8080 |
Ports are the host bind ports and are independent of the URLs — behind a reverse proxy the published port and the public URL usually differ.
Branding
Replace the Rhesis colours, icon and product name with your own. All of these are read at request time,
so the same image serves every deployment — no rebuild, and no NEXT_PUBLIC_ counterpart. Each is
independent; set only the ones you need.
These set the deployment-wide default. An organization can override any of them from Organization Settings → Branding, which also accepts uploaded favicon and font files rather than URLs. A field the organization leaves empty falls back to the variable below, and then to the Rhesis default, so a multi-tenant deployment can set a house style here and let individual organizations depart from it.
| Variable | Description | Default |
|---|---|---|
BRAND_PRIMARY_COLOR | 6-digit hex colour, e.g. #6A1B9A. Drives the primary palette, app bar, buttons, brand-tinted surfaces and the sign-in accent | (unset — Rhesis blue) |
BRAND_SECONDARY_COLOR | 6-digit hex colour for the secondary/CTA accent — secondary buttons and anything using palette.secondary | (unset — Rhesis orange) |
BRAND_FAVICON_URL | https:// URL or root-relative path, e.g. https://www.example.com/favicon.png. Used as the browser-tab icon and as the brand mark in the sidebar, onboarding and sign-in header | /logos/rhesis-logo-favicon.svg |
BRAND_PRODUCT_NAME | Name in page titles (Architect becomes Architect | Acme), the sign-in header and footer, and the sidebar before the organisation loads. Max 60 characters | Rhesis AI |
Light and dark shades are derived from each colour, and button label text switches between white and near-black to stay readable — a pale brand colour does not need extra configuration. A malformed value is ignored with a warning in the frontend logs and the Rhesis default applies, so a typo cannot break a deployment.
One difference between the two colours: the built-in secondary uses a different hue for its hover
(orange to yellow) and near-black for secondary.dark. A configured BRAND_SECONDARY_COLOR gets a plain
lighten/darken ramp instead, so secondary buttons brighten on hover within your own hue rather than
shifting to another colour.
Custom font
| Variable | Description | Default |
|---|---|---|
BRAND_FONT_FAMILY | CSS font-family name, e.g. Inria Sans. Replaces the built-in Be Vietnam Pro typeface across all UI text | (unset — Be Vietnam Pro) |
BRAND_FONT_BASE_URL | Base URL for self-hosted font files (https:// or root-relative). The app loads \{base\}/\{slug\}-300.ttf, -400.ttf and -700.ttf, where the slug is the lowercased, hyphenated family name | (unset — Google Fonts) |
When only BRAND_FONT_FAMILY is set, the font is loaded from Google Fonts. Add
BRAND_FONT_BASE_URL for deployments that cannot reach Google (air-gapped, on-prem) or prefer to
serve the files themselves. Name the .ttf files \{slug\}-\{weight\}.ttf (e.g. inria-sans-300.ttf,
inria-sans-400.ttf, inria-sans-700.ttf).
An organization can override both variables for its members from the settings page, choosing either a
Google Fonts family or its own uploaded files. Uploaded files are served from the configured object
storage (STORAGE_SERVICE_URI) through the app, so no font host needs to be reachable from the
browser; a Google family is loaded by the visitor’s browser and needs outbound access to
fonts.googleapis.com.
What to upload
For organization branding set from the settings page rather than these variables:
| Asset | Format | Size |
|---|---|---|
| Favicon | SVG, PNG, WebP, ICO, JPEG or GIF | Square. 512x512 for a raster format, or any SVG. Max 512 KB |
| Font (Google) | A family name | Any family published on Google Fonts. Weights 300, 400 and 700 are requested |
| Font (uploaded) | WOFF2, WOFF, TTF or OTF | One file per weight: 300 (light), 400 (regular), 700 (bold). Max 2 MB each |
The favicon is scaled down to the browser tab and up to 92 px in onboarding, so anything under 192x192 looks soft; the settings page warns when an uploaded icon is smaller than that or not square, but still accepts it. Uploads are checked to be real images, not just files with an image content type.
The font family field suggests names from the Google Fonts catalogue, fetched from Google and cached for a day; a deployment that cannot reach Google gets no suggestions and the field accepts any name. A Google family is checked against Google when you save, so a typo is rejected rather than silently falling back.
Uploaded fonts need only the weights you have. The app’s intermediate weights (500, 600, 800) are remapped onto 400 and 700, and the browser derives any missing weight from the nearest one it was given. The family name must match the name inside the font files.
Setting BRAND_PRODUCT_NAME also drops the Rhesis tagline from the page description and stops the
sign-in wordmark linking to rhesis.ai.
Three things keep their Rhesis identity on purpose: chart palettes, which need distinguishable hues rather than tints of one colour; the sign-in page’s background artwork; and the Documentation, Blog and GitHub links in the sign-in header.
On Kubernetes, set these through the chart instead — see Kubernetes (Helm).
Database
Defaults point at the built-in postgres container. Set these to use your own database (see
Database). DB_NAME and APP_DB_USER are fixed and
cannot be changed — SQL scripts reference them by name, so your database and role must use exactly these
values.
| Variable | Description | Default |
|---|---|---|
DB_HOST | Database host | postgres |
DB_PORT | Database port | 5432 |
DB_NAME | Database name — fixed, cannot be changed | rhesis-db |
APP_DB_USER | Runtime backend and worker role — fixed, cannot be changed | rhesis-user |
APP_DB_PASS | Password for the runtime role | rhesis-password |
ADMIN_DB_USER | Migration role; falls back to the app role when unset | (unset) |
ADMIN_DB_PASS | Password for the migration role | (unset) |
Redis and Celery
Defaults point at the built-in redis container. Set these to use an external Redis.
| Variable | Description | Default |
|---|---|---|
BROKER_URL | Celery broker connection URL | redis://:rhesis-redis-pass@redis:6379/0 |
CELERY_RESULT_BACKEND | Celery result backend URL | redis://:rhesis-redis-pass@redis:6379/1 |
AI models
Generation, evaluation, execution, and embeddings use Rhesis-hosted models by default, which require a
RHESIS_API_KEY (get one at app.rhesis.ai ). To use your own provider, set that
provider’s key and point the matching DEFAULT_*_MODEL at it.
| Variable | Description | Default |
|---|---|---|
RHESIS_API_KEY | API key for Rhesis-hosted models | (unset) |
ENABLE_RHESIS_KEY | Lets org owners configure a Rhesis platform key from the Models page instead of only RHESIS_API_KEY | true |
DEFAULT_GENERATION_MODEL | Model for test generation | rhesis/rhesis-default |
DEFAULT_EVALUATION_MODEL | Model for evaluation / judging | rhesis/rhesis-default |
DEFAULT_EXECUTION_MODEL | Model for multi-turn execution | rhesis/rhesis-default |
DEFAULT_EMBEDDING_MODEL | Model for embeddings | rhesis/rhesis-embedding |
Provider keys, read at runtime when a DEFAULT_*_MODEL targets that provider:
| Variable | Provider |
|---|---|
OPENAI_API_KEY | OpenAI |
GEMINI_API_KEY, GOOGLE_API_KEY | Google Gemini |
AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, AZURE_OPENAI_DEPLOYMENT_NAME, AZURE_OPENAI_API_VERSION | Azure OpenAI |
Supported model-provider prefixes: openai, gemini, azure, anthropic, groq, mistral,
together_ai, perplexity, replicate, openrouter, cohere, ollama. Supported embedding prefixes:
rhesis, openai, gemini, vertex_ai.
Authentication and OAuth
Without OAuth credentials, only email/password authentication is available.
| Variable | Description | Default |
|---|---|---|
AUTH_EMAIL_PASSWORD_ENABLED | Allow email/password sign-in | true |
AUTH_REGISTRATION_ENABLED | Allow new-user registration | true |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET | Enable Google sign-in (set both) | (unset) |
GH_CLIENT_ID, GH_CLIENT_SECRET | Enable GitHub sign-in (set both) | (unset) |
SMTP and email
Email (notifications, invitations) is disabled until SMTP_HOST is set.
| Variable | Description | Default |
|---|---|---|
SMTP_HOST | SMTP server host | (unset) |
SMTP_PORT | SMTP server port | 587 |
SMTP_USER | SMTP username | (unset) |
SMTP_PASSWORD | SMTP password | (unset) |
FROM_EMAIL | Default From address | noreply@example.com |
Object storage
| Variable | Description | Default |
|---|---|---|
STORAGE_SERVICE_URI | Storage backend URI (file://, gs://, or s3://) | (unset — a local directory at LOCAL_STORAGE_PATH) |
LOCAL_STORAGE_PATH | Directory used when STORAGE_SERVICE_URI is unset or file:// without a path | rhesis-files inside the system temp directory |
STORAGE_SERVICE_ACCOUNT_KEY | Base64 service-account credentials for GCS | (unset) |
LOCAL_STORAGE_PATH | Filesystem path for the file:// backend | /tmp/rhesis-files |
Enterprise features
Only used when the Enterprise package is installed (see Enterprise Features). Each feature stays disabled until its secret is set and valid.
| Variable | Description | Generate with |
|---|---|---|
SSO_ENCRYPTION_KEY | Encrypts stored SSO client_secrets; required for Single Sign-On. Must stay stable once set — changing it makes stored SSO credentials undecryptable | openssl rand -base64 32 | tr '+/' '-_' |
AUDIT_HASH_KEY | HMAC key for hashing emails in API-client audit logs; required for API Clients | openssl rand -hex 32 |
Telemetry
| Variable | Description | Default |
|---|---|---|
OTEL_RHESIS_TELEMETRY_ENABLED | Self-hosted telemetry (opt-out; set false to disable) | true |
See Telemetry System Documentation for what is collected.
Logging and tuning
| Variable | Description | Default |
|---|---|---|
LOG_LEVEL | Log verbosity | INFO |
QUICK_START | Enable Quick Start mode (auto-login; not for production) | false |
CELERY_WORKER_CONCURRENCY | Worker process concurrency | 8 |
CELERY_WORKER_PREFETCH_MULTIPLIER | Tasks prefetched per worker process | 4 |
Data retention
Two scheduled sweeps hard-delete rows past a retention window: one for job and activity_log
rows, one for trace (span) rows. Both are off by default, and both run from celery beat — with no
beat process against the backend, enabling either has no effect.
These deletes are permanent. Keep TRACE_RETENTION_DRY_RUN at true for the first run: the sweep
then logs the row count it would delete per organization, without deleting anything.
| Variable | Description | Default |
|---|---|---|
JOB_RETENTION_ENABLED | Run the job/activity_log sweep, 03:00 UTC | false |
JOB_RETENTION_DAYS | Age at which those rows are deleted | 90 |
TRACE_RETENTION_ENABLED | Run the trace sweep, 04:00 UTC | false |
TRACE_RETENTION_DRY_RUN | Count and log instead of deleting | true |
TRACE_RETENTION_DAYS | Retention window for every organization, overriding the per-plan value | (unset) |
The trace sweep reads its window from each organization’s resolved plan. Self-hosted deployments
have no usage limits, so every organization resolves to unlimited retention and the sweep deletes
nothing until TRACE_RETENTION_DAYS supplies a window. Set it to put the whole deployment on one.
Advanced
Rarely changed; the defaults are correct for most deployments.
| Variable | Description | Default |
|---|---|---|
JWT_ALGORITHM | JWT signing algorithm | HS256 |
JWT_ACCESS_TOKEN_EXPIRE_MINUTES | Access-token lifetime (minutes); refreshed automatically, and the window in which a deactivated user’s token stays valid | 15 |
RHESIS_BASE_URL | Base URL for Rhesis-hosted models | https://api.rhesis.ai |
DB_DRIVER | SQLAlchemy database driver | postgresql |
BROKER_READ_URL | Optional read-replica broker URL | (unset) |
OTEL_EXPORTER_OTLP_ENDPOINT | OTLP collector endpoint for telemetry export | https://telemetry.rhesis.ai |
OTEL_SERVICE_NAME | Reported service name | rhesis |
OTEL_DEPLOYMENT_TYPE | cloud or self-hosted label | self-hosted |
Cloud-only variables
For the Rhesis-hosted SaaS, not self-hosting. These drive the managed platform and are listed here for reference only — a self-hosted deployment does not need any of them.
Usage quota enforcement — per-organization resource limits (test executions, tracing spans, model tokens, etc.). Self-hosted deployments have no usage limits; the usage page still shows what the instance spent.
| Variable | Purpose | Default |
|---|---|---|
USAGE_QUOTAS_ENABLED | Enable per-org usage quota enforcement | false |
Onboarding and lifecycle emails — the hosted SendGrid drip campaign and welcome flow.
| Variable | Purpose |
|---|---|
SENDGRID_API_KEY | SendGrid API key |
SENDGRID_DAY_1_EMAIL_TEMPLATE_ID, SENDGRID_DAY_2_EMAIL_TEMPLATE_ID, SENDGRID_DAY_3_EMAIL_TEMPLATE_ID | Drip-campaign template IDs |
WELCOME_FROM_EMAIL, WELCOME_CALENDAR_LINK, AGENT_EMAIL_BCC, DEMO_USER_EMAIL | Welcome-email sender, calendar link, BCC, and demo recipient |
Hosted UI enrichment — frontend NEXT_PUBLIC_* values baked in at build time. When unset the UI simply
omits the extra media, so self-hosting is unaffected.
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_SUPPORT_EMAIL | Support address in the UI (falls back to hello@rhesis.ai) |
ONBOARDING_VIDEO_URL, NEXT_PUBLIC_ONBOARDING_VIDEO_URL | Onboarding welcome video |
NEXT_PUBLIC_<ENTITY>_EMPTY_STATE_VIDEO_URL | Empty-state demo video per entity |
NEXT_PUBLIC_<ENTITY>_EMPTY_STATE_ARTICLE_URLS | Empty-state help-article links per entity (comma-separated) |
The empty-state pair exists for each of TESTS, PROJECTS, TEST_SETS, TEST_RUNS, ENDPOINTS,
REQUIREMENTS, METRICS, and EXPERIMENTS.
Enterprise and GCP-hosted infrastructure — specific to Rhesis’s managed environment.
| Variable | Purpose |
|---|---|
AUTH_SECRET, DATABASE_URL | Hosted frontend auth secret and direct database URL |
VERTEX_AI_LOCATION, VERTEX_AI_PROJECT, GOOGLE_APPLICATION_CREDENTIALS | Vertex AI / GCP model access |
PERSPECTIVE_API_KEY, CHATBOT_API_KEY | Hosted third-party API keys |
DEFAULT_POLYPHEMUS_URL | Internal Rhesis access-review service |
SKIP_MIGRATIONS | Hosted runs migrations as a separate job |
RHESIS_CONNECTOR_DISABLED, RHESIS_PROJECT_ID | SDK connector settings |
WORKER_API_URL | External worker service URL |