Critical Security Variables
JWT_SECRET
Type: String (required)Used by: API server
Purpose: Secret key for signing access tokens (15-minute expiry)
Security impact: If this secret is compromised or uses a weak default value, attackers can forge access tokens and impersonate any user in the system. See the Security Best Practices documentation for details on Phase A security fixes.Generate a strong secret:
JWT_REFRESH_SECRET
Type: String (required)Used by: API server
Purpose: Secret key for signing refresh tokens (7-day expiry)
POSTGRES_PASSWORD
Type: String (required)Used by: PostgreSQL, API server
Purpose: Password for the PostgreSQL database user
The default value
fenrinegro is used in development. Never use this in production. Use a strong, randomly generated password.Database Configuration
DATABASE_URL
Type: PostgreSQL connection stringUsed by: API server, migration scripts
Purpose: Full connection string to PostgreSQL database
POSTGRES_USER
Type: StringUsed by: PostgreSQL
Default:
postgres
Redis Configuration
REDIS_URL
Type: Redis connection stringUsed by: API server
Purpose: Connection to Redis for caching and session management
In docker-compose.yml:51, this is hardcoded to
redis://redis:6379. Override this only when running outside Docker or using an external Redis instance.API Server Configuration
API_PORT
Type: NumberUsed by: API server
Default:
3001
CORS_ORIGINS
Type: Comma-separated list of URLsUsed by: API server
Default:
http://localhost:3000 (development only)
LOG_LEVEL
Type: StringUsed by: API server
Default:
infoOptions:
debug, info, warn, error
debug for development, warn or error for production.
Cloudflare R2 Storage
RestAI uses Cloudflare R2 for file storage (menu images, receipts, etc.). All R2 variables are optional but required for file upload functionality.R2_ACCOUNT_ID
Type: String (optional)Used by: API server
https://dash.cloudflare.com/{account_id}/r2
R2_ACCESS_KEY_ID
Type: String (optional)Used by: API server
R2_SECRET_ACCESS_KEY
Type: String (optional)Used by: API server
R2_BUCKET_NAME
Type: StringUsed by: API server
Default:
restai
R2_PUBLIC_URL
Type: URL (optional)Used by: API server
When
R2_PUBLIC_URL is set, all file URLs returned by the API will use this domain instead of the default *.r2.dev URL.Web Application Configuration
NEXT_PUBLIC_API_URL
Type: URL (build-time)Used by: Web frontend
Default:
http://localhost:3001 (development)
NEXT_PUBLIC_WS_URL
Type: WebSocket URL (build-time)Used by: Web frontend
Default:
ws://localhost:3001 (development)
Complete .env.example Reference
Here’s the complete.env.example file with all variables documented:
Environment Variable Checklist
Before deploying to production, verify:Security variables set
-
JWT_SECRETis set to a strong random value (not default) -
JWT_REFRESH_SECRETis set to a different strong random value -
POSTGRES_PASSWORDis changed from default - Secrets are stored securely (not committed to git)
CORS configured correctly
-
CORS_ORIGINSmatches your production domain(s) - Multiple domains are comma-separated with no spaces
- Uses HTTPS URLs (not HTTP)
API URLs in web build
-
NEXT_PUBLIC_API_URLpoints to production API -
NEXT_PUBLIC_WS_URLuseswss://(secure WebSocket) - Both URLs match your actual domain
Troubleshooting
API fails to start with “JWT_SECRET required”
Cause:JWT_SECRET or JWT_REFRESH_SECRET environment variables are missing.
Fix: Set both variables in your .env file:
CORS errors in production
Symptoms: Browser console showsCORS policy: No 'Access-Control-Allow-Origin' header
Cause: CORS_ORIGINS doesn’t include your frontend domain.
Fix: Update CORS_ORIGINS in your .env:
Web app can’t connect to API in production
Symptoms: All API requests fail with network errors Cause:NEXT_PUBLIC_API_URL still points to localhost or incorrect domain.
Fix: Rebuild the web Docker image with the correct URL:
File uploads fail
Symptoms: Menu images or receipts can’t be uploaded Cause: R2 credentials not configured or incorrect. Fix: Verify all R2 variables are set:Security Best Practices
- Never commit
.envfiles - Add.envto.gitignore - Use different secrets per environment - Development and production should have completely different JWT secrets
- Rotate secrets regularly - Change JWT secrets every 90 days
- Restrict CORS origins - Only list domains you control
- Use environment variable managers - Consider tools like HashiCorp Vault, AWS Secrets Manager, or Doppler for production
- Enable SSL for external databases - Add
?sslmode=requiretoDATABASE_URL - Audit logs regularly - Set
LOG_LEVEL=infoin production to track authentication attempts