Environment Variables¶
This document provides a comprehensive reference for all environment variables used by PulseStage.
Quick Start¶
Copy the example environment file and customize it for your deployment:
Then edit .env with your specific values.
Core Configuration¶
Database¶
| Variable | Description | Required | Default | Example |
|---|---|---|---|---|
DATABASE_URL |
PostgreSQL connection string | [OK] Yes | - | postgresql://user:pass@host:5432/dbname |
Format: postgresql://[user]:[password]@[host]:[port]/[database]?schema=[schema]
Production recommendations:
- Use connection pooling (e.g., PgBouncer)
- Enable SSL: Add ?sslmode=require to the URL
- Use a dedicated database user with minimal permissions
- Separate read replicas for analytics queries (future)
Redis¶
| Variable | Description | Required | Default | Example |
|---|---|---|---|---|
REDIS_URL |
Full Redis connection string | [WARNING] One of REDIS_URL or REDIS_HOST |
- | redis://redis:6379 |
REDIS_HOST |
Redis hostname | [WARNING] One of REDIS_URL or REDIS_HOST |
localhost |
redis |
REDIS_PORT |
Redis port | No | 6379 |
6379 |
REDIS_PASSWORD |
Redis authentication password | No | - | your-redis-password |
Used for: - Session storage - Rate limiting - Email queue (BullMQ)
Production recommendations:
- Enable authentication (requirepass in redis.conf)
- Use Redis 7+ for best performance
- Consider Redis Cluster for high availability
- Monitor memory usage (set maxmemory policy)
API Server¶
| Variable | Description | Required | Default | Example |
|---|---|---|---|---|
PORT |
API server port | No | 3000 |
3000 |
NODE_ENV |
Environment mode | No | development |
production |
CORS_ORIGIN |
Allowed CORS origins (comma-separated) | [OK] Yes | http://localhost:5173 |
https://app.yourdomain.com,https://admin.yourdomain.com |
NODE_ENV behavior:
- development: Rate limiting disabled, verbose logging
- production: Rate limiting enabled, error logging only
CORS configuration:
- Development: Use http://localhost:5173
- Production: Use your actual frontend domain(s)
- Multiple origins: Separate with commas (no spaces)
Admin Authentication¶
| Variable | Description | Required | Default | Example |
|---|---|---|---|---|
ADMIN_KEY |
Admin API key for protected endpoints | [OK] Yes | - | your-secure-random-string-min-32-chars |
[WARNING] Security:
- Generate a secure random string: openssl rand -hex 32
- Never commit this to version control
- Rotate periodically (every 90 days recommended)
- Use your secrets manager in production (AWS Secrets Manager, HashiCorp Vault, etc.)
Used for:
- /admin/* endpoints
- Health dashboard
- Email queue status
- System metrics
Multi-Tenancy¶
| Variable | Description | Required | Default | Example |
|---|---|---|---|---|
MULTI_TENANT_MODE |
Enable multi-tenant subdomain routing | No | false |
true or false |
BASE_DOMAIN |
Base domain for tenant resolution | [WARNING] Required if MULTI_TENANT_MODE=true |
- | pulsestage.com |
TENANT_HEADER |
Header name for tenant override | No | x-tenant-id |
x-tenant-slug |
Single-tenant mode (MULTI_TENANT_MODE=false):
- One organization per deployment
- Simpler setup, perfect for most use cases
- Default tenant automatically created on first startup
Multi-tenant mode (MULTI_TENANT_MODE=true):
- Multiple organizations on one deployment
- Subdomain-based routing: acme.pulsestage.com → ACME tenant
- Requires BASE_DOMAIN to be set
- See Multi-Tenancy Architecture for details
Tenant resolution order (when MULTI_TENANT_MODE=true):
1. x-tenant-id or x-tenant-slug header (if present)
2. Subdomain extraction from Host header
3. Falls back to default tenant
Email Configuration¶
See Email Configuration Guide for complete setup instructions.
Core Email Settings¶
| Variable | Description | Required | Default | Example |
|---|---|---|---|---|
EMAIL_PROVIDER |
Email service to use | [OK] Yes | - | smtp or resend |
EMAIL_FROM |
Sender email address | [OK] Yes | - | noreply@yourdomain.com |
EMAIL_FROM_NAME |
Sender display name | No | PulseStage |
Your Company AMA |
FRONTEND_URL |
Frontend URL for email links | [OK] Yes | - | https://yourdomain.com |
SMTP Configuration¶
Only required when EMAIL_PROVIDER=smtp.
| Variable | Description | Required | Default | Example |
|---|---|---|---|---|
SMTP_HOST |
SMTP server hostname | [OK] Yes | - | smtp.sendgrid.net |
SMTP_PORT |
SMTP server port | [OK] Yes | - | 587 |
SMTP_SECURE |
Use TLS (true for 465, false for 587) | [OK] Yes | - | true or false |
SMTP_USER |
SMTP username | [WARNING] Usually required | - | apikey |
SMTP_PASS |
SMTP password | [WARNING] Usually required | - | SG.xxxxx |
Common SMTP providers:
- SendGrid: smtp.sendgrid.net:587, user=apikey, pass=SG.xxxxx
- AWS SES: email-smtp.us-east-1.amazonaws.com:587, SMTP credentials from IAM
- Mailgun: smtp.mailgun.org:587, username/password from Mailgun
- Gmail (dev only): smtp.gmail.com:587, use App Password
Resend Configuration¶
Only required when EMAIL_PROVIDER=resend.
| Variable | Description | Required | Default | Example |
|---|---|---|---|---|
RESEND_API_KEY |
Resend API key | [OK] Yes | - | re_xxxxxxxxxxxxx |
Setup:
1. Sign up at resend.com
2. Verify your sending domain
3. Generate API key (starts with re_)
Server-Sent Events (SSE)¶
| Variable | Description | Required | Default | Example |
|---|---|---|---|---|
SSE_HEARTBEAT_INTERVAL |
Heartbeat interval in milliseconds | No | 30000 (30s) |
15000 |
Purpose: Keep SSE connections alive through proxies/load balancers.
Recommendations: - Default (30s) works for most deployments - Lower (15s) if connections drop frequently - Higher (60s) to reduce server load (if stable network)
Frontend Configuration¶
| Variable | Description | Required | Default | Example |
|---|---|---|---|---|
WEBSITE_TITLE |
Browser tab title | No | PulseStage |
Your Company AMA |
Note: Frontend-specific variables are configured in web/.env (not shown here). See web/README.md.
Development-Only Variables¶
These are used for local development and testing. Do not use in production.
| Variable | Description | Default | Example |
|---|---|---|---|
MOCK_AUTH |
Enable mock SSO authentication | false |
true |
LOG_LEVEL |
Logging verbosity | info |
debug |
Example Configurations¶
Local Development (docker-compose.override.yaml)¶
environment:
DATABASE_URL: postgresql://app:app@db:5432/ama
REDIS_URL: redis://redis:6379
CORS_ORIGIN: http://localhost:5173
NODE_ENV: development
ADMIN_KEY: dev-admin-key
# Email (Mailpit)
EMAIL_PROVIDER: smtp
SMTP_HOST: mailpit
SMTP_PORT: 1025
SMTP_SECURE: false
EMAIL_FROM: noreply@pulsestage.local
FRONTEND_URL: http://localhost:5173
# Single-tenant mode
MULTI_TENANT_MODE: false
Production (AWS/DigitalOcean/etc.)¶
# Database
DATABASE_URL=postgresql://pulsestage:SECURE_PASSWORD@db.internal:5432/pulsestage?sslmode=require
# Redis
REDIS_URL=redis://:SECURE_REDIS_PASSWORD@redis.internal:6379
# API
PORT=3000
NODE_ENV=production
CORS_ORIGIN=https://ama.yourcompany.com
ADMIN_KEY=<generated-with-openssl-rand-hex-32>
# Email (Resend)
EMAIL_PROVIDER=resend
RESEND_API_KEY=re_xxxxxxxxxxxx
EMAIL_FROM=noreply@yourcompany.com
EMAIL_FROM_NAME=Company AMA
FRONTEND_URL=https://ama.yourcompany.com
# Single-tenant
MULTI_TENANT_MODE=false
Production Multi-Tenant (SaaS)¶
# Database
DATABASE_URL=postgresql://pulsestage:SECURE_PASSWORD@db.internal:5432/pulsestage?sslmode=require
# Redis
REDIS_URL=redis://:SECURE_REDIS_PASSWORD@redis.internal:6379
# API
PORT=3000
NODE_ENV=production
CORS_ORIGIN=https://*.pulsestage.com
ADMIN_KEY=<generated-with-openssl-rand-hex-32>
# Email (Resend)
EMAIL_PROVIDER=resend
RESEND_API_KEY=re_xxxxxxxxxxxx
EMAIL_FROM=noreply@pulsestage.com
EMAIL_FROM_NAME=PulseStage
FRONTEND_URL=https://pulsestage.com
# Multi-tenant
MULTI_TENANT_MODE=true
BASE_DOMAIN=pulsestage.com
Security Best Practices¶
1. Never Commit Secrets¶
# [ERROR] Bad: Committed .env file
git add .env
git commit -m "Add config"
# [OK] Good: .env is in .gitignore
git add env.example
git commit -m "Add example config"
2. Use Secrets Management¶
Development:
- Store in .env file (already ignored by git)
- Share securely via 1Password, LastPass, etc.
Production: - AWS: Secrets Manager, Systems Manager Parameter Store - Kubernetes: Secrets, External Secrets Operator - Docker Swarm: Docker Secrets - Ansible: Ansible Vault
3. Rotate Credentials Regularly¶
| Credential | Rotation Frequency |
|---|---|
ADMIN_KEY |
Every 90 days |
| Database password | Every 180 days |
| Redis password | Every 180 days |
| Email API keys | Every 180 days or on compromise |
4. Principle of Least Privilege¶
- Database user: Only
SELECT,INSERT,UPDATE,DELETE(noDROP,ALTER) - Redis: Use ACLs if available (Redis 6+)
- Email API keys: Restricted to "Mail Send" permission only
Validation¶
Check your configuration is correct:
# Check API starts without errors
docker compose up -d api
docker compose logs api --tail 50
# Verify database connection
docker compose exec api npx prisma db pull
# Verify Redis connection
docker compose exec api node -e "const Redis = require('ioredis'); const r = new Redis({host:'redis'}); r.ping().then(console.log)"
# Verify email configuration
curl http://localhost:3000/admin/email-queue \
-H "X-Admin-Key: your-admin-key"
Troubleshooting¶
API won't start¶
Check logs:
Common issues:
- DATABASE_URL malformed → Check syntax
- Can't connect to Redis → Verify REDIS_URL and Redis is running
- ADMIN_KEY not set → Add to environment variables
CORS errors in browser¶
Symptom: Console shows "CORS policy: No 'Access-Control-Allow-Origin' header"
Fix: Ensure CORS_ORIGIN matches your frontend URL exactly.
# Development
CORS_ORIGIN=http://localhost:5173
# Production
CORS_ORIGIN=https://ama.yourcompany.com
Emails not sending¶
See Email Configuration Guide.
Reference¶
For more information: - Production Deployment Guide - Email Configuration - Multi-Tenancy Architecture - Security Overview