Skip to content

Self-hosting

Stept is a FastAPI backend + React frontend, shipped as prebuilt images. The production stack is Docker Compose behind Caddy (automatic HTTPS).

ServiceRole
apiFastAPI app (uvicorn, N workers)
workerARQ task queue worker — AI runs, ingestion/crawling, webhook delivery
schedulerexactly one loop for periodic jobs (SLA breaches, scheduled syncs) — never scale past 1
webnginx serving the dashboard SPA + the embeddable widget bundle
postgresPostgres 16 with pgvector
redistask queue + rate limiting + realtime pub/sub
caddythe single public entry point, terminates TLS via Let’s Encrypt

The reference docker-compose.prod.yml, Caddyfile and an annotated env.example live in the repo’s deploy/ directory.

Terminal window
mkdir -p /opt/stept && cd /opt/stept
# copy docker-compose.prod.yml as docker-compose.yml, Caddyfile, and env.example as .env
# then edit .env:
# DOMAIN, APP_DOMAIN, SERVER_IP, ACME_EMAIL
# STEPT_SECRET_KEY ← python -c 'import secrets; print(secrets.token_urlsafe(48))'
# POSTGRES_PASSWORD
# STEPT_PUBLIC_BASE_URL / STEPT_APP_BASE_URL = https://<your app domain>
docker compose pull
docker compose run --rm api migrate # apply database migrations first
docker compose up -d

Check health:

Terminal window
curl https://<your-app-domain>/api/v1/healthz
# {"status":"ok","version":"...","database":"ok"}

With STEPT_ENV=prod the app refuses to boot if the secret key is a known default or shorter than 32 characters, or if a base URL is plain http://. Fail-loud beats silently signing tokens with a public constant.

Two things to know about STEPT_SECRET_KEY:

  1. It signs every JWT (sessions, widget tokens, password resets).
  2. The encryption key for stored secrets (AI provider keys, channel tokens) is derived from it — rotating it invalidates all stored provider credentials.

For local hacking you don’t need any of this: the backend defaults to SQLite and an in-process task queue.

Terminal window
cd backend && uv run uvicorn app.main:app --port 8600 --reload
cd frontend && pnpm dev

Model API keys are not instance configuration — each workspace adds its own provider keys in the dashboard, encrypted at rest. The instance never needs an LLM key in .env.