Local development
Run the stores in Docker and the services from the repository.
Configuration
Copy the example environment of every service:
for app in api api-ingest mcp worker-errors worker-jobs web; do cp apps/$app/.env.example apps/$app/.env; doneSet BETTER_AUTH_SECRET in apps/api/.env to at least 32 characters: openssl rand -hex 32. Leave optional variables out rather than empty: KEY= fails validation. Every variable is in Configuration.
Stores
docker compose up -dPostgreSQL on 5432, ClickHouse on 8123, Valkey for state on 6379 and cache on 6380, RustFS on 9000, all on 127.0.0.1 only. A root .env overrides ports and credentials.
Dependencies and schema
bun install
bun run --cwd apps/api db:migrate
bun run --cwd apps/api-ingest db:migrateServices
Start each in its own terminal: bun run dev:api-ingest, dev:api, dev:worker-errors, dev:worker-jobs, dev:web, and dev:mcp for AI assistants.
| Service | URL |
|---|---|
web |
https://openperf.localhost |
api |
https://api.openperf.localhost |
api-ingest |
https://ingest.openperf.localhost |
mcp |
https://mcp.openperf.localhost |
Services run behind portless. The first run asks for sudo once to bind port 443 and trust its certificate. Node processes outside portless that call these URLs need NODE_EXTRA_CA_CERTS=~/.portless/ca.pem. PORTLESS=0 runs a service on its own port: then point the origins in its .env at http://localhost and remove BETTER_AUTH_COOKIE_DOMAIN.
bun run seed --project <id|slug>fills a project with demo telemetry. SetINGEST_RATE_LIMIT_PER_MINUTE=0andINGEST_IP_RATE_LIMIT_PER_MINUTE=0inapps/api-ingest/.envso it isn’t throttled.bun run dev:docsserves this site on port 4300.