Project: Splenda CMS (Strapi v5) Date: 2025-11-21 Purpose: Complete reference for infrastructure deployment configuration
- Quick Reference
- Build Time vs Runtime
- Variables by Priority
- Variables by Category
- Environment-Specific Configurations
- Indispensable: 11 variables (app won't start without these)
- Necessary: 9 variables (prevents security warnings, enables core features)
- Optional: 15 variables (fine-tuning and optional features)
- Total: 35 variables
- Build Time: 0 variables (all TypeScript compilation happens at build time, but doesn't require env vars)
- Runtime: All 35 variables (read when application starts)
None required.
The application uses TypeScript configs that are compiled during Docker build (tsconfig.config.json), but these don't depend on environment variables. The compilation step (npm run compile:config) happens in Dockerfile.prod using static source files.
All variables are runtime variables. They are read when:
- Strapi server starts (
npm run start) - Configuration files are loaded (
config/*.tsor compiledconfig-dist/*.js) - Application bootstraps and connects to services
Application will not start without these variables.
| Variable | Build/Runtime | Type | Example | Description |
|---|---|---|---|---|
APP_KEYS |
Runtime | String (CSV) | key1,key2,key3,key4 |
Required. 4 comma-separated base64 keys for session encryption. Generate with openssl rand -base64 32 (4 times). |
JWT_SECRET |
Runtime | String | abc123... |
Required. Secret for JWT token signing (user authentication). Generate with openssl rand -base64 32. |
ADMIN_JWT_SECRET |
Runtime | String | xyz789... |
Required. Secret for admin panel JWT tokens. Generate with openssl rand -base64 32. |
API_TOKEN_SALT |
Runtime | String | def456... |
Required. Salt for API token hashing. Generate with openssl rand -base64 32. |
TRANSFER_TOKEN_SALT |
Runtime | String | ghi789... |
Required. Salt for data transfer tokens. Generate with openssl rand -base64 32. |
ENCRYPTION_KEY |
Runtime | String | jkl012... |
Required. Encryption key for API tokens visibility in admin panel. Generate with openssl rand -base64 32. |
DATABASE_HOST |
Runtime | String | postgres or xxx.db.ondigitalocean.com |
Required. PostgreSQL server hostname. |
DATABASE_PORT |
Runtime | Integer | 5432 or 25060 |
Required. PostgreSQL server port. |
DATABASE_NAME |
Runtime | String | strapi or staging_main |
Required. PostgreSQL database name. |
DATABASE_USERNAME |
Runtime | String | strapi or doadmin |
Required. PostgreSQL username. |
DATABASE_PASSWORD |
Runtime | String | secure-password |
Required. PostgreSQL password. |
Generation Command:
# Generate all secrets at once
for i in {1..6}; do openssl rand -base64 32; done
# First 4 outputs → APP_KEYS (join with commas, no spaces)
# Output 5 → JWT_SECRET
# Output 6 → ADMIN_JWT_SECRET
# Run again for remaining secrets (API_TOKEN_SALT, TRANSFER_TOKEN_SALT, ENCRYPTION_KEY)Application will start but with warnings or limited functionality.
| Variable | Build/Runtime | Type | Example | Description |
|---|---|---|---|---|
NODE_ENV |
Runtime | String | production |
Necessary. Environment mode. Use production for staging/prod, development for local. Default: development. |
STRAPI_TELEMETRY_DISABLED |
Runtime | Boolean | true |
Necessary. Disable Strapi analytics. Recommended true for production to avoid data leaks. |
VERIFICATION_CODE_CHARSET |
Runtime | String | alphanumeric-safe |
Necessary. Charset for verification codes. Without this, shows security warning. Options: numeric | alphanumeric-safe. Recommended: alphanumeric-safe. |
VERIFICATION_CODE_LENGTH |
Runtime | Integer | 6 |
Necessary. Code length. Without this, shows security warning. Recommended: 6 or 8. |
SMTP_HOST |
Runtime | String | smtp.sendgrid.net or mailpit |
Necessary for email. SMTP server hostname. Falls back to Mailpit if not configured. |
SMTP_PORT |
Runtime | Integer | 587 or 1025 |
Necessary for email. SMTP port. 587 for SendGrid, 1025 for Mailpit. |
SMTP_FROM |
Runtime | String | [email protected] |
Necessary for email. Sender email address. Must be verified in SendGrid for production. |
SMTP_REPLY_TO |
Runtime | String | [email protected] |
Necessary for email. Reply-to email address. |
DATABASE_SSL |
Runtime | Boolean | true |
Necessary for managed DBs. Enable SSL for PostgreSQL connection. Required for DigitalOcean managed databases. |
Application works fully without these, but they enable specific features or fine-tuning.
| Variable | Build/Runtime | Type | Example | Description |
|---|---|---|---|---|
SMTP_USERNAME |
Runtime | String | apikey |
Optional. SMTP auth username. For SendGrid, use literal string "apikey". Omit for Mailpit. |
SMTP_PASSWORD |
Runtime | String | SG.xxx... |
Optional. SMTP auth password. For SendGrid, use API key. Omit for Mailpit. |
| Variable | Build/Runtime | Type | Example | Description |
|---|---|---|---|---|
TWILIO_ACCOUNT_SID |
Runtime | String | ACxxxxxxxx... |
Optional. Twilio Account SID. Required only if using SMS verification. |
TWILIO_AUTH_TOKEN |
Runtime | String | your_token... |
Optional. Twilio Auth Token. Required only if using SMS verification. |
TWILIO_PHONE_NUMBER |
Runtime | String | +1234567890 |
Optional. Twilio phone number in E.164 format. Required only if using SMS verification. |
| Variable | Build/Runtime | Type | Example | Description |
|---|---|---|---|---|
SPACES_BUCKET |
Runtime | String | splenda-uploads |
Optional. S3 bucket name. If not set, uses local filesystem for uploads. |
SPACES_REGION |
Runtime | String | nyc3 |
Optional. DigitalOcean Spaces region. Default: nyc3. |
SPACES_ACCESS_KEY |
Runtime | String | DO00XXXXX... |
Optional. Spaces access key. Required if SPACES_BUCKET is set. |
SPACES_SECRET_KEY |
Runtime | String | xxxxx... |
Optional. Spaces secret key. Required if SPACES_BUCKET is set. |
SPACES_PREFIX |
Runtime | String | staging/ or pr-123/ |
Optional. Prefix for uploaded files. Enables environment isolation in shared bucket. |
| Variable | Build/Runtime | Type | Example | Description |
|---|---|---|---|---|
HOST |
Runtime | String | 0.0.0.0 |
Optional. Server bind address. Default: 0.0.0.0 (all interfaces). |
PORT |
Runtime | Integer | 1337 |
Optional. Server listen port. Default: 1337. |
| Variable | Build/Runtime | Type | Example | Description |
|---|---|---|---|---|
DATABASE_SSL_REJECT_UNAUTHORIZED |
Runtime | Boolean | false |
Optional. Verify SSL certificate. Use false for DigitalOcean managed DBs. Default: false. |
DATABASE_POOL_MIN |
Runtime | Integer | 2 |
Optional. Minimum database connection pool size. Default: 2. |
DATABASE_POOL_MAX |
Runtime | Integer | 10 |
Optional. Maximum database connection pool size. Default: 10. |
DATABASE_CONNECTION_TIMEOUT |
Runtime | Integer | 60000 |
Optional. Database connection timeout (ms). Default: 60000. |
| Variable | Build/Runtime | Type | Example | Description |
|---|---|---|---|---|
ADMIN_ACCESS_TOKEN_LIFESPAN |
Runtime | Integer | 1800 |
Optional. Admin access token lifespan (seconds). Default: 1800 (30 min). |
ADMIN_MAX_REFRESH_TOKEN_LIFESPAN |
Runtime | Integer | 2592000 |
Optional. Max refresh token lifespan (seconds). Default: 2592000 (30 days). |
ADMIN_IDLE_REFRESH_TOKEN_LIFESPAN |
Runtime | Integer | 604800 |
Optional. Idle refresh token lifespan (seconds). Default: 604800 (7 days). |
ADMIN_MAX_SESSION_LIFESPAN |
Runtime | Integer | 2592000 |
Optional. Max session lifespan (seconds). Default: 2592000 (30 days). |
ADMIN_IDLE_SESSION_LIFESPAN |
Runtime | Integer | 3600 |
Optional. Idle session lifespan (seconds). Default: 3600 (1 hour). |
| Variable | Build/Runtime | Type | Example | Description |
|---|---|---|---|---|
VERIFICATION_CODE_EXPIRATION_MINUTES |
Runtime | Integer | 10 |
Optional. Code expiration time (minutes). Default: 10. Does not trigger warning. |
| Variable | Build/Runtime | Type | Example | Description |
|---|---|---|---|---|
FLAG_NPS |
Runtime | Boolean | true |
Optional. Enable NPS survey in admin panel. Default: true. |
FLAG_PROMOTE_EE |
Runtime | Boolean | true |
Optional. Promote Enterprise Edition features. Default: true. |
WEBHOOKS_POPULATE_RELATIONS |
Runtime | Boolean | false |
Optional. Populate relations in webhook payloads. Default: false. |
| Variable | Build/Runtime | Type | Example | Description |
|---|---|---|---|---|
ENVIRONMENT_TYPE |
Runtime | String | staging or production |
Optional. Environment identifier for logging/monitoring. No functional impact. |
APP_KEYS⚠️ RequiredJWT_SECRET⚠️ RequiredADMIN_JWT_SECRET⚠️ RequiredAPI_TOKEN_SALT⚠️ RequiredTRANSFER_TOKEN_SALT⚠️ RequiredENCRYPTION_KEY⚠️ Required
Indispensable:
DATABASE_HOST⚠️ RequiredDATABASE_PORT⚠️ RequiredDATABASE_NAME⚠️ RequiredDATABASE_USERNAME⚠️ RequiredDATABASE_PASSWORD⚠️ Required
Optional:
DATABASE_SSL(Necessary for managed DBs)DATABASE_SSL_REJECT_UNAUTHORIZEDDATABASE_POOL_MINDATABASE_POOL_MAXDATABASE_CONNECTION_TIMEOUT
Necessary:
SMTP_HOST(Falls back to Mailpit)SMTP_PORT(Falls back to Mailpit)SMTP_FROM(Falls back to default)SMTP_REPLY_TO(Falls back to default)
Optional:
SMTP_USERNAME(Required only for SendGrid)SMTP_PASSWORD(Required only for SendGrid)
TWILIO_ACCOUNT_SID(Required only if using SMS)TWILIO_AUTH_TOKEN(Required only if using SMS)TWILIO_PHONE_NUMBER(Required only if using SMS)
Necessary:
VERIFICATION_CODE_CHARSET(Prevents security warning)VERIFICATION_CODE_LENGTH(Prevents security warning)
Optional:
VERIFICATION_CODE_EXPIRATION_MINUTES
SPACES_BUCKET(Enables cloud storage)SPACES_REGIONSPACES_ACCESS_KEY(Required if SPACES_BUCKET set)SPACES_SECRET_KEY(Required if SPACES_BUCKET set)SPACES_PREFIX
HOSTPORT
ADMIN_ACCESS_TOKEN_LIFESPANADMIN_MAX_REFRESH_TOKEN_LIFESPANADMIN_IDLE_REFRESH_TOKEN_LIFESPANADMIN_MAX_SESSION_LIFESPANADMIN_IDLE_SESSION_LIFESPAN
Necessary:
NODE_ENVSTRAPI_TELEMETRY_DISABLED
Optional:
ENVIRONMENT_TYPEFLAG_NPSFLAG_PROMOTE_EEWEBHOOKS_POPULATE_RELATIONS
Minimal configuration (11 indispensable variables):
# Secrets (generate with: for i in {1..6}; do openssl rand -base64 32; done)
APP_KEYS=key1,key2,key3,key4
JWT_SECRET=<generated>
ADMIN_JWT_SECRET=<generated>
API_TOKEN_SALT=<generated>
TRANSFER_TOKEN_SALT=<generated>
ENCRYPTION_KEY=<generated>
# Database (Docker Compose)
DATABASE_HOST=postgres
DATABASE_PORT=5432
DATABASE_NAME=strapi
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=strapiRecommended configuration (+9 necessary variables):
# Add to minimal config above:
NODE_ENV=development
STRAPI_TELEMETRY_DISABLED=true
# Email (Mailpit - no auth)
SMTP_HOST=mailpit
SMTP_PORT=1025
[email protected]
[email protected]
# Verification
VERIFICATION_CODE_CHARSET=alphanumeric-safe
VERIFICATION_CODE_LENGTH=6
VERIFICATION_CODE_EXPIRATION_MINUTES=10
# Database
DATABASE_SSL=falseTotal: 20 variables (11 indispensable + 9 necessary)
Required for staging (20 core variables + SendGrid + Spaces):
# Secrets (same as production, generated once)
APP_KEYS=key1,key2,key3,key4
JWT_SECRET=<generated>
ADMIN_JWT_SECRET=<generated>
API_TOKEN_SALT=<generated>
TRANSFER_TOKEN_SALT=<generated>
ENCRYPTION_KEY=<generated>
# Environment
NODE_ENV=production
STRAPI_TELEMETRY_DISABLED=true
ENVIRONMENT_TYPE=staging
# Database (Managed PostgreSQL)
DATABASE_HOST=xxx.db.ondigitalocean.com
DATABASE_PORT=25060
DATABASE_NAME=staging_main
DATABASE_USERNAME=doadmin
DATABASE_PASSWORD=<from-terraform>
DATABASE_SSL=true
DATABASE_SSL_REJECT_UNAUTHORIZED=false
# Email (SendGrid for real delivery)
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USERNAME=apikey
SMTP_PASSWORD=SG.xxx...
[email protected]
[email protected]
# Verification
VERIFICATION_CODE_CHARSET=alphanumeric-safe
VERIFICATION_CODE_LENGTH=6
VERIFICATION_CODE_EXPIRATION_MINUTES=10
# File Storage (DigitalOcean Spaces)
SPACES_BUCKET=splenda-uploads
SPACES_REGION=nyc3
SPACES_ACCESS_KEY=DO00XXXXX...
SPACES_SECRET_KEY=<from-terraform>
SPACES_PREFIX=staging/Optional for staging (SMS testing):
# SMS (Twilio - if testing SMS verification)
TWILIO_ACCOUNT_SID=ACxxxxxxxx...
TWILIO_AUTH_TOKEN=<from-twilio>
TWILIO_PHONE_NUMBER=+1234567890Total: 30-33 variables (with/without SMS)
Same as staging, but with environment-specific values:
# All variables same as staging, except:
DATABASE_NAME=pr_123 # PR-specific database
SPACES_PREFIX=pr-123/ # PR-specific file prefix
ENVIRONMENT_TYPE=pr-preview # Metadata
# Email: Use Mailpit (not SendGrid) to avoid costs
SMTP_HOST=mailpit
SMTP_PORT=1025
[email protected]
[email protected]
# Omit SMTP_USERNAME and SMTP_PASSWORDTotal: 27-30 variables (Mailpit instead of SendGrid)
All recommended variables (staging config + SMS + fine-tuning):
# Same as staging, plus:
# Environment
ENVIRONMENT_TYPE=production
NODE_ENV=production
# SMS (Real Twilio account, not trial)
TWILIO_ACCOUNT_SID=ACxxxxxxxx...
TWILIO_AUTH_TOKEN=<production-token>
TWILIO_PHONE_NUMBER=+1234567890
# Email (Production SendGrid with verified domain)
[email protected]
[email protected]
# File Storage
SPACES_PREFIX=production/
# Database
DATABASE_NAME=production
# Security hardening
VERIFICATION_CODE_LENGTH=8 # Longer codes for production
VERIFICATION_CODE_EXPIRATION_MINUTES=10
# Session configuration (optional fine-tuning)
ADMIN_IDLE_SESSION_LIFESPAN=1800 # 30 min instead of 1 hourTotal: 33-35 variables (all features enabled)
Generate ALL secrets before deployment:
# Generate 6 secrets for core Strapi configuration
for i in {1..6}; do openssl rand -base64 32; done
# Use first 4 for APP_KEYS (comma-separated, no spaces):
# APP_KEYS=secret1,secret2,secret3,secret4
# Use output 5 for JWT_SECRET
# Use output 6 for ADMIN_JWT_SECRET
# Run again for remaining secrets:
for i in {1..3}; do openssl rand -base64 32; done
# Output 1 → API_TOKEN_SALT
# Output 2 → TRANSFER_TOKEN_SALT
# Output 3 → ENCRYPTION_KEY- Never commit secrets to git -
.envis in.gitignore - Use environment-specific secrets - Different secrets for dev/staging/production
- Rotate secrets periodically - Especially after team member departures
- Use secret management tools - DigitalOcean App Platform's encrypted environment variables
- Restrict access - Only infrastructure admins should see production secrets
-
Verify sender before deploying:
- Settings → Sender Authentication → Single Sender Verification
- Verify
[email protected]
-
Use restricted API keys:
- SendGrid Dashboard → API Keys → Create API Key
- Permissions: Mail Send only (not full access)
- Never use the same API key for dev/staging/production
-
Test before production:
- Use staging environment to test real email delivery
- Verify emails arrive in inbox (not spam)
- Test email templates render correctly
- Enable SSL for managed databases:
DATABASE_SSL=true - Use strong passwords: Generate with
openssl rand -base64 32 - Restrict database access: Firewall rules to allow only App Platform IPs
- Separate databases per environment:
staging_main,production, etc.
- All 11 indispensable variables are set
- All secrets generated with
openssl rand -base64 32 -
APP_KEYShas exactly 4 comma-separated keys (no spaces) -
NODE_ENV=productionfor staging/production -
STRAPI_TELEMETRY_DISABLED=truefor staging/production - Database credentials tested (can connect)
- SendGrid API key verified (test email sent successfully)
- SendGrid sender email verified in SendGrid dashboard
- Spaces credentials tested (can upload file)
-
VERIFICATION_CODE_CHARSETandVERIFICATION_CODE_LENGTHset (no warning) - SSL enabled for managed database (
DATABASE_SSL=true)
- Application starts without errors
- No security warnings in logs
- Admin panel accessible at
/admin - Database connection successful
- Email delivery works (registration, password reset)
- File uploads work (if Spaces configured)
- API tokens visible in admin panel (verifies
ENCRYPTION_KEY) - User registration → verification → login flow works
Check indispensable variables:
# Verify all 11 required variables are set:
echo "APP_KEYS: ${APP_KEYS:0:10}..." # Should show first 10 chars
echo "JWT_SECRET: ${JWT_SECRET:0:10}..."
echo "ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET:0:10}..."
echo "API_TOKEN_SALT: ${API_TOKEN_SALT:0:10}..."
echo "TRANSFER_TOKEN_SALT: ${TRANSFER_TOKEN_SALT:0:10}..."
echo "ENCRYPTION_KEY: ${ENCRYPTION_KEY:0:10}..."
echo "DATABASE_HOST: $DATABASE_HOST"
echo "DATABASE_PORT: $DATABASE_PORT"
echo "DATABASE_NAME: $DATABASE_NAME"
echo "DATABASE_USERNAME: $DATABASE_USERNAME"
echo "DATABASE_PASSWORD: ${DATABASE_PASSWORD:0:5}..." # First 5 chars onlySet verification code variables:
VERIFICATION_CODE_CHARSET=alphanumeric-safe
VERIFICATION_CODE_LENGTH=6Check SMTP configuration:
echo "SMTP_HOST: $SMTP_HOST"
echo "SMTP_PORT: $SMTP_PORT"
echo "SMTP_USERNAME: $SMTP_USERNAME"
echo "SMTP_PASSWORD: ${SMTP_PASSWORD:0:5}..." # First 5 chars
echo "SMTP_FROM: $SMTP_FROM"For SendGrid:
- Verify sender email in SendGrid dashboard
- Check API key is valid and has "Mail Send" permission
- Ensure
SMTP_USERNAME=apikey(literal string) - Ensure
SMTP_PASSWORDstarts withSG.
Check database variables and SSL:
echo "DATABASE_HOST: $DATABASE_HOST"
echo "DATABASE_PORT: $DATABASE_PORT"
echo "DATABASE_SSL: $DATABASE_SSL"
echo "DATABASE_SSL_REJECT_UNAUTHORIZED: $DATABASE_SSL_REJECT_UNAUTHORIZED"
# Test connection manually:
psql -h $DATABASE_HOST -p $DATABASE_PORT -U $DATABASE_USERNAME -d $DATABASE_NAMEFor DigitalOcean managed databases:
- Must use
DATABASE_SSL=true - Must use
DATABASE_SSL_REJECT_UNAUTHORIZED=false - Port is usually
25060, not5432
Check Spaces configuration:
echo "SPACES_BUCKET: $SPACES_BUCKET"
echo "SPACES_REGION: $SPACES_REGION"
echo "SPACES_ACCESS_KEY: ${SPACES_ACCESS_KEY:0:10}..."
echo "SPACES_PREFIX: $SPACES_PREFIX"
# Test Spaces access with AWS CLI:
aws s3 ls s3://$SPACES_BUCKET \
--endpoint-url https://${SPACES_REGION}.digitaloceanspaces.com \
--profile digitalocean.env.example: Template with all variables and commentsdocs/environment-variables.md: Detailed documentation for admin session configurationdocs/EMAIL_CONFIGURATION.md: Complete email setup guide- ADR-002: TypeScript config compilation strategy
- ADR-005: Email configuration architecture
- ADR-006: SMS and Email verification strategy
- 2025-11-21: Initial report created for staging deployment
- Variables verified against codebase:
config/*.ts - Total: 35 environment variables documented