Production deployment checklist
This page covers the settings and first-run steps for deploying core-api with
deployment/docker-compose.yml (started with docker compose --env-file .env.local up --build -d
from the deployment/ directory). core-api owns users, passwords, sessions, MFA, invites, API
keys and roles (see Identity and authentication), so a fresh database has
no users until you create the first admin.
1. Pre-flight
| Item | Requirement |
|---|---|
JWT_SECRET | Required outside ENVIRONMENT=local (>= 32 chars, not the dev default or containing change_me): openssl rand -hex 32. The app refuses to start otherwise. Changing it later signs everyone out. |
MFA_ENCRYPTION_KEY | Required outside local (>= 32 chars): openssl rand -hex 32. Changing it later makes enrolled authenticators unusable. |
DATABASE_URL | Must include a password outside local. The compose file builds it from POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB. |
DASHBOARD_URL | The public https URL of the dashboard. It is used in emailed links and makes session cookies Secure (override with COOKIE_SECURE). |
CORS_ORIGINS | Must include the dashboard origin. |
TRUST_PROXY_HEADERS | Set true only if a reverse proxy/tunnel in front of the API overwrites X-Forwarded-For. When it is false behind a proxy, every user appears to share the proxy's IP, so the per-IP login limit (50 per 5 min) is shared by everyone. |
DOCS_PUBLIC_URL | The docs service serves this documentation site (static build behind nginx). It publishes no host port: route a reverse proxy on the compose network to docs:8080. DOCS_PUBLIC_URL is the origin readers reach it at through that proxy; it is baked into the build (canonical URLs, sitemap), so changing it needs up --build docs. The service has no dependencies and can be left out of a deployment. |
| Mail transport | SMTP_HOST/SMTP_PORT must point to a real provider. The default Mailpit only captures mail, so password-reset, invite and sign-in-code emails would never arrive. |
docker compose --env-file .env.local config fails fast if the two secrets are unset.
The shipped deployment/.env.local carries placeholders that the app rejects on purpose.
2. Deploy and create the first admin
-
Bring the stack up. The
migratorservice runsalembic upgrade headand exits; the API starts after it succeeds. On start the API creates the platformsuperadminrole if it is missing (ensure_platform_rolesinapp/main.py). -
Create the first admin so someone can sign in:
BOOTSTRAP_PASSWORD='<strong passphrase>' \uv run python scripts/bootstrap_admin.py --email [email protected]# or: mise run user:bootstrap-admin [email protected]Run it inside the API container (
docker compose exec api ...) so it uses production settings. By default the user becomes asuperadmin; use--tenant <id> --role <role key>to grant a tenant role instead, and--reset-passwordto change an existing user's password. The same script is the break-glass recovery path. -
Sign in, then invite users (
/invites); they can also use Forgot password (needs working mail). Subscribers sign in with an emailed code.
3. Verification checklist
GET /api/v1/healthreturns 200 and the API log shows no settings validation error.POST /api/v1/auth/loginfor the bootstrap admin returns{"ok": true}withapp.at/app.rtcookies scoped to the API host andSecureset.GET /api/v1/melists the expectedroles(superadminfor the bootstrap admin) andpermissions.- Trigger Forgot password for a test user and confirm the email actually arrives.
- Enrol MFA on a test account, sign out and back in. Superadmins need MFA to use platform-admin
endpoints (
SUPERADMIN_REQUIRES_MFA). - Repeated failed logins from one address return 429 with
Retry-After(and that address is the client's, not the proxy's - checkTRUST_PROXY_HEADERS).
4. Policies worth knowing
- Passwords must be at least 12 characters (
PASSWORD_MIN_LENGTHinapp/core/security.py). - Five failed logins within 15 minutes lock an account for 15 minutes (
LOCKOUT_*settings). - Tenant create/rename/delete and the console require a platform role such as
superadmin; a tenant's own roles are managed by its tenant admins - see Roles and scopes. - New users are invited by emailed link; administrators never email passwords.