Skip to main content

Deployment

Compose files

Local (loads docker-compose.override.yml, which publishes Postgres on 55432):

docker compose up -d --build

Production — base file only, so the Postgres host port is not published:

docker compose -f docker-compose.yml up -d --build
ServicePort
Portal3000
Gateway8080
Docs3002
Databaseinternal only in production
Redisinternal only

Hard requirements

  • AUTH_DEV_BYPASS must be unset or false in production
  • TLS: gateway and portal speak plain HTTP and must sit behind a TLS-terminating reverse proxy. The gateway streams SSE — disable proxy response buffering and allow long response times for /v1/chat/completions
  • Redis has no authentication in compose. Keep it on the internal network, or set a password/TLS via REDIS_URL
  • Do not reuse any values from a developer's local .env

Database migrations

The portal applies pending database migrations automatically on startup. A normal deploy needs no manual database step.

If you see a missing-column (or missing-table) error after a deploy, pull the latest release — it must include the matching migration — and redeploy. Never patch the database by hand. Migrations are forward-only; rollbacks ship as new migrations.

Probes

EndpointUse
GET /health/livelinessLiveness — process is up
GET /health/readinessReadiness — Postgres, and Redis when REDIS_URL is set

Invite emails (SMTP)

SMTP_HOST=smtp.office365.com
SMTP_PORT=587
SMTP_USER=zeallm@zealogics.com
SMTP_PASS=<app password>
SMTP_FROM=zeallm@zealogics.com

Without SMTP_HOST, invites still create the account.

Microsoft Entra ID SSO

  1. Azure portal → App registration (single tenant)
  2. Web redirect URI: https://<host>/api/auth/callback/microsoft-entra-id (local: http://localhost:3000/api/auth/callback/microsoft-entra-id)
  3. Set AZURE_AD_CLIENT_ID, AZURE_AD_CLIENT_SECRET, AZURE_AD_TENANT_ID, ZEALLM_ALLOWED_DOMAIN, ZEALLM_ADMIN_EMAILS
  4. AUTH_DEV_BYPASS=false

The “Continue with Microsoft” button appears once all three AZURE_AD_* vars are set. Default openid profile email scopes are enough.

Load testing

A load-test script lives in loadtest/. Point it at the gateway URL, a revealed virtual key, and a model the key can access.