Local environment setup
End-to-end checklist for a new engineer (or agent) to get FitKit running on a fresh machine.
Prerequisites
- Node 20.x (matches
@types/node@20.19.9) pnpm10.29+ —corepack enable && corepack prepare pnpm@10.29.1 --activate- Docker + docker compose (Postgres + Redis run in containers)
- A Clerk dev instance (free tier is enough)
- Cloudflare R2 bucket for file storage (optional in pure local; required to exercise uploads, exports, forms PDFs)
1. Clone & install
git clone git@github.com:desmotech/fitkit.git
cd fitkit
pnpm install2. Infrastructure (Postgres + Redis)
Two stacks live in docker-compose.yml:
| Service | Port | Purpose |
|---|---|---|
postgres | 5432 | Dev DB (fitkit_dev). pgvector extension preinstalled (used by the embeddings module — libs/db/src/lib/schema/_pgvector.ts). |
redis | 6379 | BullMQ, Socket.IO adapter, Spotter rate limit / cache |
make dev-up # postgres + redis
make dev-db-migrateThe test stack lives in docker-compose.test.yml and runs Postgres on port 55432 to avoid colliding with dev. See testing/strategy.md.
3. Backend env (apps/api/.env)
Copy .env.example and fill the [REQUIRED] entries. Minimum to boot:
DATABASE_URL— defaults to local docker.REDIS_URL— defaults to local docker.CLERK_SECRET_KEY— from Clerk dashboard → API Keys.FRONTEND_URL=http://localhost:3000ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3002PAYMENT_CREDENTIALS_ENCRYPTION_KEY— 32-byte hex. Generate:node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"NATIONAL_ID_ENCRYPTION_KEY— same.CLERK_WEBHOOK_SECRET— only needed if you wire up Clerk webhooks locally (use ngrok).
Do not commit apps/api/.env. It’s gitignored.
Optional but commonly needed
| Var | Why you’d set it |
|---|---|
R2_* | File uploads, exports, forms PDFs. Without them the upload pipeline degrades. |
RESEND_API_KEY | Outbound email. re_test_dummy works for unit/integration tests; for real send-test you need a real key. |
CRONS_ENABLED=true | Enable scheduled jobs (billing retry, no-show sweeps, AI snapshots). Default off so dev DB stays cold. |
CARDCOM_*, MORNING_*, RIVHIT_*, ICREDIT_* | Payments providers. Sandbox URLs in .env.example. |
VOYAGE_API_KEY | Exercise embedding enrichment. Without it, similarity search returns empty. |
ANTHROPIC_API_KEY | LLM enrichment scripts + Spotter agent. Without it the agent endpoints will fail. |
SENTRY_DSN | Error tracking. Leave empty to disable. |
4. Frontend env (apps/web/.env.local)
Minimum:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY— pair to the secret key above.NEXT_PUBLIC_API_URL=http://localhost:3001NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-inNEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
Optional: NEXT_PUBLIC_SENTRY_DSN, NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_GOOGLE_MAPS_API_KEY.
5. Admin env (apps/admin/.env.local) — only if developing on the admin app
VITE_API_URL=http://localhost:3001VITE_CLERK_PUBLISHABLE_KEY=...
6. Boot
pnpm dev # API on 3001, web on 3000 (concurrently)
# or one at a time:
pnpm dev:api
pnpm dev:webSmoke-test:
- API health: http://localhost:3001/health
- Swagger: http://localhost:3001/api/docs
- Web: http://localhost:3000 → redirected to
/en(or yourAccept-Language).
7. Seed data (optional)
pnpm seed:movements # canonical movement library
pnpm enrich:aliases # only if VOYAGE_API_KEY + ANTHROPIC_API_KEY are setEnd-to-end seed for E2E tests is handled by testApi.seedX() helpers under apps/web/e2e/. See testing/strategy.md.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
relation "..." does not exist on API boot | DB not migrated | make dev-db-migrate |
CORS: origin not allowed | Web origin missing from ALLOWED_ORIGINS | Add http://localhost:3000 (and admin if used) |
too many clients from pg | Pool size mismatched to DB plan | See DB_POOL_MAX notes in .env.example |
| Clerk session not persisted on the web | Mismatched publishable/secret keys, or middleware running on a non-locale path | Confirm both .env files reference the same Clerk instance |
| Forms PDF render fails locally | Puppeteer can’t find Chromium | Set PUPPETEER_EXECUTABLE_PATH to your local Chrome (/Applications/Google Chrome.app/Contents/MacOS/Google Chrome on macOS) |
_journal.json when complaint | Stale migration after merge | Regenerate the stale migration so its when exceeds the latest applied. See migrations.md. |