Environment Variables
Complete reference for all environment variables used in Cerberus IAM API.
Overview
Cerberus uses environment variables for all configuration. Variables are:
- Validated - Zod schema validation at startup
- Type-safe - TypeScript types derived from schema
- Documented - Each variable has a clear purpose
- Defaulted - Sensible defaults where applicable
Quick Start
- Copy the example file:
cp .env.example .env- Generate required secrets:
# Generate SECRET_ENCRYPTION_KEY
node scripts/generate-secret-encryption-key.jsUpdate database connection and other variables
Validate configuration:
npm run dev
# Application will exit if configuration is invalidVariable Reference
Server Configuration
NODE_ENV
- Type:
'development' | 'test' | 'production' - Default:
'development' - Description: Application environment
NODE_ENV=productionEffects:
- Logging format (pretty in dev, JSON in production)
- Error messages (verbose in dev, concise in production)
- Source maps, debugging features
PORT
- Type:
number - Default:
4000 - Description: HTTP server port
PORT=4000Notes:
- Must be between 1024-65535 for non-root users
- Use 4000 for development
- Cloud platforms often override this (e.g., Heroku sets
PORT)
ISSUER_URL
- Type:
string(URL) - Required: Yes
- Description: OAuth2/OIDC issuer URL
ISSUER_URL=https://auth.yourcompany.comRequirements:
- Must be a valid URL with protocol
- Should match your public-facing domain
- Used in JWT
issclaim and OIDC discovery
Examples:
# Development
ISSUER_URL=http://localhost:4000
# Production
ISSUER_URL=https://auth.acme.comDatabase Configuration
DATABASE_URL
- Type:
string - Required: Yes
- Description: PostgreSQL connection string
DATABASE_URL=postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMAFormat:
postgresql://[user[:password]@][host][:port][/dbname][?param=value&...]Examples:
# Development (with password)
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/cerberus_iam?schema=public
# Development (peer authentication)
DATABASE_URL=postgresql://jerome@localhost:5432/cerberus_iam?schema=public
# Production (connection pooling)
DATABASE_URL=postgresql://user:[email protected]:5432/cerberus?schema=public&connection_limit=10&pool_timeout=20
# Cloud database (SSL)
DATABASE_URL=postgresql://user:[email protected]:5432/db?schema=public&sslmode=requireConnection Pooling: Prisma manages connection pooling automatically. Additional parameters:
connection_limit- Max connections (default: unlimited)pool_timeout- Connection timeout in seconds (default: 10)sslmode- SSL mode (disable,prefer,require)
CORS Configuration
ADMIN_WEB_ORIGIN
- Type:
string(URL) - Optional: Yes
- Description: Public-facing admin web origin
ADMIN_WEB_ORIGIN=https://admin.yourcompany.comNotes:
- Used for CORS allow-list
- Must include protocol and port
- Set to admin web app URL
ADMIN_WEB_INTERNAL_ORIGIN
- Type:
string(URL) - Optional: Yes
- Description: Internal/Docker network admin web origin
ADMIN_WEB_INTERNAL_ORIGIN=http://admin-web:3000Use Cases:
- Docker Compose deployments
- Kubernetes internal networking
- Server-to-server communication
LOGIN_UI_URL
- Type:
string(URL) - Optional: Yes
- Description: Absolute URL to the hosted login experience
LOGIN_UI_URL=https://login.yourcompany.com/sign-inBehavior:
- When set, unauthenticated authorization requests (
/oauth2/authorize) redirect here - The API appends a
redirect_uriquery parameter pointing back to the original issuer URL - Leave unset to use the built-in
/auth/loginroute (useful for development or legacy flows)
Cryptography & JWT
JWT_ALG
- Type:
'EdDSA' | 'RS256' - Default:
'EdDSA' - Description: JWT signing algorithm
JWT_ALG=EdDSAAlgorithms:
EdDSA- Ed25519, faster, smaller keys (recommended)RS256- RSA, widely supported, larger keys
JWKS_ROTATE_DAYS
- Type:
number - Default:
30 - Description: Days between automatic key rotation
JWKS_ROTATE_DAYS=30Recommendations:
- Development: 30 days
- Production: 30-90 days
- High-security: 7-14 days
SECRET_ENCRYPTION_KEY
- Type:
string(base64) - Required: Yes
- Description: AES-256-GCM encryption key for secrets
SECRET_ENCRYPTION_KEY=jcQ9C71Fqxj4E/n54rcu/87bXOQ4YN96lQu/LO449oY=Generation:
node scripts/generate-secret-encryption-key.jsRequirements:
- Must be base64-encoded 32-byte key
- Keep secure and never commit to version control
- Rotate periodically (requires data migration)
Used For:
- Webhook secrets
- TOTP secrets
- OAuth client secrets (future)
- Any encrypted database fields
Logging Configuration
SERVICE_NAME
- Type:
string - Default:
'cerberus-iam-api' - Description: Service name in logs
SERVICE_NAME=cerberus-iam-apiLOG_LEVEL
- Type:
'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' - Default:
'info' - Description: Minimum log level
LOG_LEVEL=infoLevels (from highest to lowest):
fatal- Application-ending errorserror- Errors requiring attentionwarn- Warning conditionsinfo- General informational messagesdebug- Debugging informationtrace- Very verbose, trace-level logs
Recommendations:
- Development:
debugortrace - Staging:
info - Production:
infoorwarn
LOG_REMOTE_URL
- Type:
string(URL) - Optional: Yes
- Description: Remote log aggregation endpoint
LOG_REMOTE_URL=https://logs.yourcompany.com/ingestSupported Services:
- Custom HTTP endpoint
- Elasticsearch
- Datadog
- New Relic
- Any HTTP log collector
LOG_REMOTE_API_KEY
- Type:
string - Optional: Yes
- Description: API key for remote logging service
LOG_REMOTE_API_KEY=your-api-key-hereLOG_REMOTE_BATCH_SIZE
- Type:
number - Default:
50 - Description: Number of logs to batch before sending
LOG_REMOTE_BATCH_SIZE=50LOG_REMOTE_FLUSH_INTERVAL_MS
- Type:
number - Default:
5000 - Description: Milliseconds between batch flushes
LOG_REMOTE_FLUSH_INTERVAL_MS=5000Session Configuration
SESSION_COOKIE_NAME
- Type:
string - Default:
'cerb_sid' - Description: Session cookie name
SESSION_COOKIE_NAME=cerb_sidSESSION_COOKIE_SECURE
- Type:
boolean - Default:
false - Description: Require HTTPS for session cookies
SESSION_COOKIE_SECURE=trueImportant:
- Set to
truein production - Requires HTTPS connection
- Prevents cookie transmission over HTTP
SESSION_COOKIE_DOMAIN
- Type:
string - Default:
'localhost' - Description: Cookie domain attribute
SESSION_COOKIE_DOMAIN=.yourcompany.comExamples:
# Development
SESSION_COOKIE_DOMAIN=localhost
# Production (allow subdomains)
SESSION_COOKIE_DOMAIN=.acme.com
# Production (single domain)
SESSION_COOKIE_DOMAIN=auth.acme.comEmail Configuration
EMAIL_FROM
- Type:
string(email) - Required: Yes
- Description: Sender email address
EMAIL_FROM=[email protected]SMTP_HOST
- Type:
string - Required: Yes
- Description: SMTP server hostname
SMTP_HOST=smtp.sendgrid.netExamples:
# Development (Mailhog)
SMTP_HOST=localhost
# SendGrid
SMTP_HOST=smtp.sendgrid.net
# AWS SES
SMTP_HOST=email-smtp.us-east-1.amazonaws.com
# Gmail
SMTP_HOST=smtp.gmail.comSMTP_PORT
- Type:
number - Required: Yes
- Description: SMTP server port
SMTP_PORT=587Common Ports:
25- Unencrypted (not recommended)587- STARTTLS (recommended)465- SSL/TLS1025- Mailhog development
SMTP_USER
- Type:
string - Optional: Yes
- Default:
'' - Description: SMTP username
SMTP_USER=apikeySMTP_PASS
- Type:
string - Optional: Yes
- Default:
'' - Description: SMTP password
SMTP_PASS=your-smtp-passwordSecurity:
- Never commit SMTP credentials
- Use environment-specific secrets
- Rotate credentials regularly
Security & Rate Limiting
RATE_WINDOW_SEC
- Type:
number - Default:
60 - Description: Default rate limit window (seconds)
RATE_WINDOW_SEC=60RATE_MAX
- Type:
number - Default:
120 - Description: Max requests per window
RATE_MAX=120AUTH_RATE_WINDOW_SEC
- Type:
number - Default:
60 - Description: Authentication rate limit window
AUTH_RATE_WINDOW_SEC=60AUTH_RATE_MAX
- Type:
number - Default:
30 - Description: Max auth requests per window
AUTH_RATE_MAX=30Protected Endpoints:
/v1/auth/login/v1/auth/register/v1/auth/forgot-password/v1/auth/reset-password
TOKEN_RATE_WINDOW_SEC
- Type:
number - Default:
60 - Description: Token endpoint rate limit window
TOKEN_RATE_WINDOW_SEC=60TOKEN_RATE_MAX
- Type:
number - Default:
30 - Description: Max token requests per window
TOKEN_RATE_MAX=30Protected Endpoints:
/oauth2/token
Environment-Specific Examples
Development
# .env.development
NODE_ENV=development
PORT=4000
ISSUER_URL=http://localhost:4000
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/cerberus_iam?schema=public
ADMIN_WEB_ORIGIN=http://localhost:5173
ADMIN_WEB_INTERNAL_ORIGIN=http://localhost:5173
JWT_ALG=EdDSA
JWKS_ROTATE_DAYS=30
SECRET_ENCRYPTION_KEY=jcQ9C71Fqxj4E/n54rcu/87bXOQ4YN96lQu/LO449oY=
LOG_LEVEL=debug
SESSION_COOKIE_NAME=cerb_sid
SESSION_COOKIE_SECURE=false
SESSION_COOKIE_DOMAIN=localhost
EMAIL_FROM=[email protected]
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_USER=
SMTP_PASS=
RATE_MAX=1000
AUTH_RATE_MAX=100
TOKEN_RATE_MAX=100Production
# .env.production
NODE_ENV=production
PORT=4000
ISSUER_URL=https://auth.acme.com
DATABASE_URL=postgresql://cerberus:[email protected]:5432/cerberus_production?schema=public&sslmode=require&connection_limit=20
ADMIN_WEB_ORIGIN=https://admin.acme.com
ADMIN_WEB_INTERNAL_ORIGIN=http://admin-web:3000
JWT_ALG=EdDSA
JWKS_ROTATE_DAYS=30
SECRET_ENCRYPTION_KEY=PRODUCTION_SECRET_KEY_BASE64
SERVICE_NAME=cerberus-iam-api
LOG_LEVEL=info
LOG_REMOTE_URL=https://logs.acme.com/ingest
LOG_REMOTE_API_KEY=prod_log_api_key
LOG_REMOTE_BATCH_SIZE=100
LOG_REMOTE_FLUSH_INTERVAL_MS=10000
SESSION_COOKIE_NAME=cerb_sid
SESSION_COOKIE_SECURE=true
SESSION_COOKIE_DOMAIN=.acme.com
EMAIL_FROM=[email protected]
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASS=SENDGRID_API_KEY
RATE_WINDOW_SEC=60
RATE_MAX=120
AUTH_RATE_WINDOW_SEC=60
AUTH_RATE_MAX=30
TOKEN_RATE_WINDOW_SEC=60
TOKEN_RATE_MAX=30Validation & Troubleshooting
Configuration Validation
The application validates all environment variables at startup. If validation fails:
Invalid environment configuration:
{
"DATABASE_URL": ["Required"],
"ISSUER_URL": ["Invalid url"],
"PORT": ["Expected number, received nan"],
"SECRET_ENCRYPTION_KEY": ["SECRET_ENCRYPTION_KEY must be a base64-encoded 32-byte key"]
}Common Issues
Database Connection Fails
Error: Can't reach database server
Solutions:
- Verify
DATABASE_URLformat - Check database is running
- Test connection:
psql $DATABASE_URL - Verify network connectivity
- Check SSL requirements
Invalid Encryption Key
Error: SECRET_ENCRYPTION_KEY must be a base64-encoded 32-byte key
Solution:
node scripts/generate-secret-encryption-key.jsCORS Errors
Error: Origin not allowed by CORS policy
Solutions:
- Set
ADMIN_WEB_ORIGINto exact client URL - Include protocol (http/https)
- Include port if non-standard
- Restart server after changes
Security Best Practices
Never Commit Secrets
bash# Add to .gitignore .env .env.local .env.productionUse Different Keys Per Environment
bash# Development SECRET_ENCRYPTION_KEY=dev_key_here # Production SECRET_ENCRYPTION_KEY=prod_key_differentRotate Secrets Regularly
SECRET_ENCRYPTION_KEY- Annually (requires migration)SMTP_PASS- QuarterlyLOG_REMOTE_API_KEY- Quarterly
Validate in CI/CD
yaml# GitHub Actions - name: Validate Config run: | cp .env.example .env npm run validate-config
Next Steps
- Configuration Guide - Middleware and app configuration
- Database Setup - Database configuration and migrations
- Production Deployment - Production environment setup