Environment Variables
All environment variables are declared in bun-env.d.ts for type-safe access via Bun.env. Copy .env.example to .env and fill in the required values before running the server.
Bun loads .env automatically — dotenv is not used.
Never commit .env to source control.
Required Variables
The server calls validateEnv() at startup and exits immediately if any of these are missing.
| Variable | Type | Used by | Purpose |
|---|---|---|---|
POSTGRES_SERVER | string | backend/db/client.ts | PostgreSQL hostname (e.g. localhost) |
POSTGRES_DB | string | backend/db/client.ts | Database name |
POSTGRES_USER | string | backend/db/client.ts | Database username |
POSTGRES_PASSWORD | string | backend/db/client.ts | Database password |
JWT_SECRET | string | backend/utils/auth/ | HMAC-SHA256 secret for signing/verifying JWTs |
APP_URL | string | backend/services/authService.ts | Base URL used in invite links (e.g. https://example.com) |
Optional Variables
These are safe to omit; the related feature is disabled or uses a sensible default.
Server
| Variable | Default | Used by | Purpose |
|---|---|---|---|
NODE_ENV | — | backend/server.ts | production enables prod SPA serving; else dev mode |
PORT | 3210 | backend/server.ts | HTTP listen port |
CORS_ORIGIN | — | backend/utils/cors.ts | Comma-separated allowed origins, or * |
DISABLE_RATE_LIMIT | — | backend/middleware/rateLimitMiddleware.ts | Set to "true" to bypass rate limiting (E2E tests) |
Seed
| Variable | Default | Used by | Purpose |
|---|---|---|---|
SEED_ADMIN_EMAIL | — | backend/db/seed.ts | Email address of the seeded admin user |
SEED_ADMIN_PASSWORD | — | backend/db/seed.ts | Password for the seeded admin user |
SEED_ADMIN_NAME | — | backend/db/seed.ts | Display name of the seeded admin user |
E2E Tests
| Variable | Default (baked in) | Used by | Purpose |
|---|---|---|---|
E2E_TEST_EMAIL | — | e2e/global-setup.ts | Test user email for Playwright runs |
E2E_TEST_PASSWORD | — | e2e/global-setup.ts | Test user password for Playwright runs |
E2E_TEST_NAME | — | e2e/global-setup.ts | Test user display name |
OpenTelemetry (opt-in)
Feature is disabled when OTEL_ENDPOINT is absent. See backend/features/telemetry/README.md.
| Variable | Default | Used by | Purpose |
|---|---|---|---|
OTEL_ENDPOINT | — | backend/features/telemetry/ | OTLP HTTP base URL; absence disables all backend OTel |
OTEL_SERVICE_NAME | bun-boiler | backend/features/telemetry/ | Service name in all backend signals |
BUN_PUBLIC_OTEL_SERVICE_NAME | — | frontend/telemetry/telemetry.ts | Service name for browser spans; absence disables frontend OTel |
SMTP Email (opt-in)
Feature is disabled when SMTP_HOST is absent. See backend/features/mail/README.md.
| Variable | Default | Used by | Purpose |
|---|---|---|---|
SMTP_HOST | — | backend/features/mail/ | SMTP hostname; absence disables all email sending |
SMTP_PORT | 587 | backend/features/mail/ | SMTP port |
SMTP_USER | — | backend/features/mail/ | SMTP auth username (auth skipped if absent) |
SMTP_PASS | — | backend/features/mail/ | SMTP auth password (auth skipped if absent) |
SMTP_FROM | no-reply@localhost | backend/features/mail/ | Default From address |
SMTP_SECURE | false | backend/features/mail/ | Set to "true" for implicit TLS (port 465) |
OpenPanel Analytics (opt-in, frontend)
Feature is disabled when BUN_PUBLIC_OPENPANEL_CLIENT_ID is absent. See frontend/features/analytics/README.md.
BUN_PUBLIC_ prefix is required for Bun to expose variables to the frontend bundle at build time.
| Variable | Default | Used by | Purpose |
|---|---|---|---|
BUN_PUBLIC_OPENPANEL_CLIENT_ID | — | frontend/features/analytics/ | OpenPanel project Client ID; absence disables analytics |
BUN_PUBLIC_OPENPANEL_API_URL | — | frontend/features/analytics/ | OpenPanel collector URL (for self-hosted instances) |
Runtime Config Injection
Frontend variables (BUN_PUBLIC_*) are normally baked into the bundle at build time. For deployments that supply env vars at runtime (e.g. Coolify, Docker without build args), backend/serveProdBuild.ts injects a window.__APP_CONFIG__ object into the HTML at request time:
<script>
window.__APP_CONFIG__ = {
BUN_PUBLIC_OPENPANEL_CLIENT_ID: '...',
BUN_PUBLIC_OPENPANEL_API_URL: '...',
};
</script>The frontend reads from window.__APP_CONFIG__ first, then falls back to import.meta.env.
Adding a New Variable
- Add the variable to
.env.example(no value — just the key and a comment) - Add a typed declaration to
bun-env.d.tsin theBun.Envinterface - If it’s a
BUN_PUBLIC_*variable also add it toImportMetaEnvandWindow.__APP_CONFIG__inbun-env.d.ts - Update this document