Skip to content
Tikab's Toolkit

Environment variables

The complete environment surface. The guiding rule: required things crash loudly at boot; optional things degrade a feature.

Required

VariableMeaning
DATABASE_URLPostgres connection string
BETTER_AUTH_SECRETsession signing secret (openssl rand -hex 32)
BETTER_AUTH_URLthe app's external URL — keeps cookies correct

Storage

VariableMeaning
S3_ENDPOINTMinIO/S3 endpoint (default http://localhost:9000)
S3_ACCESS_KEY / S3_SECRET_KEYcredentials
S3_BUCKETbucket (default uploads)
AZURE_STORAGE_ACCOUNTswitches the driver to Azure Blob
AZURE_STORAGE_KEY / AZURE_STORAGE_CONTAINERBlob credentials + container

Optional services (graceful degradation)

VariableTurns onWithout it
HATCHET_CLIENT_TOKENbackground jobs (jobs)enqueues log-and-skip
HATCHET_CLIENT_TLS_STRATEGY=noneplaintext gRPC against local hatchet-liteSDK tries TLS and fails ("h2 is not supported")
SMTP_HOST (+ SMTP_PORT, SMTP_SECURE, SMTP_USER, SMTP_PASS, MAIL_FROM)real email deliveryoutbox only
OPENROUTER_API_KEYhosted LLMAI features fall back to stubs
OLLAMA_BASE_URLlocal LLM (wins over hosted)
AI_MODELmodel override for the active providerprovider default
VALKEY_URLcache + shared auth rate-limit storecache no-ops; rate limit is per-process
METRICS_ENABLEDPrometheus metrics + HTTP histogram (monitoring)/api/metrics answers 404
TRUSTED_ORIGINSextra allowed origins behind a proxy (security)only BETTER_AUTH_URL is trusted
LDAP_ENABLED (+ LDAP_URL, LDAP_BIND_DN, LDAP_BIND_PASSWORD, LDAP_USER_SEARCH_BASE)LDAP sign-in (enterprise)the directory button is hidden
OIDC_DISCOVERY_URL (+ OIDC_CLIENT_ID, OIDC_CLIENT_SECRET)OIDC SSO (enterprise)the SSO button is hidden
CENTRIFUGO_TOKEN_HMAC_SECRET + CENTRIFUGO_API_KEY (+ CENTRIFUGO_API_URL, CENTRIFUGO_WS_URL)realtime server-push (realtime)@repo/realtime no-ops
COLLAB_WS_URLcollaborative editing (collab)editors run local-only (no sync)

Compose-only (service wiring)

VariableMeaning
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB / POSTGRES_PORTthe Postgres container
MINIO_ROOT_USER / MINIO_ROOT_PASSWORD / MINIO_API_PORT / MINIO_CONSOLE_PORT / MINIO_BUCKETthe MinIO container
GRAFANA_PORT / PROMETHEUS_PORTthe monitoring profile's containers
HATCHET_DASHBOARD_PORT / HATCHET_POSTGRES_PORTthe Hatchet containers
VALKEY_PORTthe Valkey container
CENTRIFUGO_PORTthe Centrifugo container (--profile realtime)
LLDAP_LDAP_PORT / LLDAP_UI_PORT / DEX_PORTthe enterprise-IdP containers (--profile idp)
CADDY_PORTthe Caddy front door (--profile proxy)
COLLAB_PORTthe Yjs sync server (--profile collab)
PGWEB_PORTthe database browser (--profile tools)

The template, live

example/.env.example is the canonical, always-current documentation:

# Template for `example/.env` — the values docker-compose substitutes.
# Copy to `.env` and adjust. For local dev the defaults below are fine.
 
# Postgres
POSTGRES_USER=toolkit
POSTGRES_PASSWORD=toolkit_local_pw
POSTGRES_DB=toolkit
POSTGRES_PORT=5433
 
# MinIO (S3-compatible object storage)
MINIO_ROOT_USER=toolkit
MINIO_ROOT_PASSWORD=toolkit_local_pw
MINIO_API_PORT=9000
MINIO_CONSOLE_PORT=9001
MINIO_BUCKET=uploads
 
# pgweb (offline DB-browser, opt-in): docker compose --profile tools up -d pgweb
PGWEB_PORT=8081
 
# Azure Blob (alternative storage driver). Locally MinIO above is used; the
# Azure deploy sets these as app settings via Bicep (infra/main.bicep), which
# switches @repo/storage to the Blob driver. Only fill them in
# here to test that driver against a real storage account.
AZURE_STORAGE_ACCOUNT=
AZURE_STORAGE_KEY=
AZURE_STORAGE_CONTAINER=uploads
 
# Hatchet (background jobs). Optional — leave unset to boot without background
# processing (uploads simply aren't enqueued). Bring up the engine with
# `docker compose up -d`, then mint a token from the dashboard
# (http://localhost:8080 → Settings → API Tokens) or the admin CLI (see
# docker-compose.yml) and paste it into `.env.local`. The Hatchet engine and
# dashboard run on their own Postgres (separate from the app DB above).
HATCHET_CLIENT_TOKEN=
# Local hatchet-lite speaks plaintext gRPC — without this the SDK tries TLS
# and fails with "h2 is not supported".
HATCHET_CLIENT_TLS_STRATEGY=none
HATCHET_CLIENT_HOST_PORT=localhost:7077
HATCHET_DASHBOARD_PORT=8080
HATCHET_POSTGRES_PORT=5435
 
# AI provider (@repo/ai). Optional — with neither set, chat endpoints return a
# clear error and briefing/document-analysis fall back to stubs. OLLAMA_BASE_URL
# wins when both are set (explicit on-prem intent). AI_MODEL overrides the
# per-provider default (OpenRouter: anthropic/claude-sonnet-4.5, Ollama: llama3.1).
# Real values belong in `.env.local`.
# Hosted:            OPENROUTER_API_KEY=sk-or-...
# On-prem/air-gapped: OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_BASE_URL=
AI_MODEL=
 
# Real email delivery (nodemailer). Optional — leave SMTP_HOST unset and every
# mail is only recorded to the in-app outbox (mailbox table + console), which
# is the dev default. Same graceful-degradation contract as Hatchet above.
# Real values belong in `.env.local`, like other secrets.
# Hosted (Mailgun):    SMTP_HOST=smtp.mailgun.org  SMTP_PORT=587
# SMTP_USER=postmaster@<domain>  SMTP_PASS=<smtp password>
# On-prem/air-gapped:  SMTP_HOST=<internal relay>  (often no SMTP_USER/PASS)
SMTP_HOST=
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=
SMTP_PASS=
MAIL_FROM="Toolkit <no-reply@example.com>"
 
# ── Cache / rate-limit store (optional) ─────────────────────────────
# Valkey from the compose default profile. Without it @repo/cache no-ops and
# auth rate limits fall back to per-process memory.
VALKEY_URL=redis://localhost:6380
VALKEY_PORT=6380
 
# ── Monitoring (optional) ───────────────────────────────────────────
# Exposes Prometheus metrics at /api/metrics and turns on the HTTP
# histogram middleware. Scraped by the compose monitoring profile:
# docker compose --profile monitoring up -d
METRICS_ENABLED=true
# Extra origins trusted by Better Auth when behind a proxy (comma-separated).
TRUSTED_ORIGINS=
GRAFANA_PORT=3001
PROMETHEUS_PORT=9090
 
# ── Enterprise sign-in (optional) ───────────────────────────────────
# Local IdPs as code:  docker compose --profile idp up -d
# LDAP — directory sign-in (lldap locally, AD in production)
LDAP_ENABLED=true
LDAP_URL=ldap://localhost:3890
LDAP_BIND_DN=uid=admin,ou=people,dc=example,dc=com
LDAP_BIND_PASSWORD=admin_lldap_pw
LDAP_USER_SEARCH_BASE=ou=people,dc=example,dc=com
LDAP_ATTR_USERNAME=uid
# OIDC — SSO (Dex locally; Entra ID / ADFS / Keycloak in production)
OIDC_DISCOVERY_URL=http://localhost:5556/dex/.well-known/openid-configuration
OIDC_CLIENT_ID=toolkit
OIDC_CLIENT_SECRET=toolkit-dev-secret
LLDAP_LDAP_PORT=3890
LLDAP_UI_PORT=17170
DEX_PORT=5556
 
# ── Realtime / server-push (optional) ───────────────────────────────
# Centrifugo from the realtime profile:  docker compose --profile realtime up -d
# Secrets must match the engine's (see docker-compose.yml).
CENTRIFUGO_TOKEN_HMAC_SECRET=local-dev-centrifugo-hmac-secret
CENTRIFUGO_API_KEY=local-dev-centrifugo-api-key
CENTRIFUGO_API_URL=http://localhost:8000/api
CENTRIFUGO_WS_URL=ws://localhost:8000/connection/websocket
CENTRIFUGO_PORT=8000
 
# ── Collaborative editing (optional) ────────────────────────────────
# Yjs websocket sync server:  docker compose --profile collab up -d
# Without it @repo/collab falls back to a local-only doc (no sync).
COLLAB_WS_URL=ws://localhost:1234
COLLAB_PORT=1234