Environment Variables Reference
Complete reference for all environment variables used in ShopySeed across development, staging, and production.
Complete reference for all environment variables used in ShopySeed. This guide covers development, staging, and production configurations.
Environment Files
| File | Purpose | When to Use |
|---|---|---|
apps/api/.env | API development settings | Local development |
apps/api/.env.production | API production settings | Production deployment |
apps/web/.env.local | Web development settings | Local development |
apps/web/.env.production | Web production settings | Production deployment |
.env.production | Global production settings | Docker Compose production |
Backend API Variables (apps/api/.env)
Application Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
NODE_ENV | ✅ | development | Application environment (development, staging, production) |
PORT | ❌ | 3011 | Port for the API server to listen on |
LOG_LEVEL | ❌ | debug | Logging level (debug, info, warn, error) |
API_PREFIX | ❌ | api | Global API route prefix |
Examples:
# Development
NODE_ENV=development
PORT=3011
LOG_LEVEL=debug
API_PREFIX=api
# Production
NODE_ENV=production
PORT=3011
LOG_LEVEL=info
API_PREFIX=apiDatabase Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL | ✅ | - | Complete PostgreSQL connection string |
POSTGRES_USER | ✅ | - | PostgreSQL username |
POSTGRES_PASSWORD | ✅ | - | PostgreSQL password |
POSTGRES_DB | ✅ | - | PostgreSQL database name |
POSTGRES_HOST | ❌ | localhost | PostgreSQL host |
POSTGRES_PORT | ❌ | 5433 | PostgreSQL port |
Examples:
# Development
DATABASE_URL=postgresql://postgres:postgres@localhost:5433/shopyseed_dev?schema=public
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=shopyseed_dev
POSTGRES_HOST=localhost
POSTGRES_PORT=5433
# Production
DATABASE_URL=postgresql://postgres:STRONG_PASSWORD@db-host:5432/shopyseed_prod?schema=public
POSTGRES_USER=postgres
POSTGRES_PASSWORD=STRONG_PASSWORD_HERE
POSTGRES_DB=shopyseed_prod
POSTGRES_HOST=db-host
POSTGRES_PORT=5432Notes:
DATABASE_URLtakes precedence over individual connection variables- Use
schema=publicfor the main schema, tenant schemas are managed automatically - Connection pooling is handled by Prisma automatically
Redis Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
REDIS_URL | ❌ | - | Complete Redis connection string |
REDIS_HOST | ❌ | localhost | Redis host |
REDIS_PORT | ❌ | 6379 | Redis port |
REDIS_PASSWORD | ❌ | - | Redis password (if required) |
REDIS_DB | ❌ | 0 | Redis database number |
Examples:
# Development (no auth)
REDIS_URL=redis://localhost:6379
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
# Production (with auth)
REDIS_URL=redis://username:password@redis-host:6379/0
REDIS_HOST=redis-host
REDIS_PORT=6379
REDIS_PASSWORD=redis-password
REDIS_DB=0Notes:
- Redis is optional but recommended for production
- Used for session storage and caching
- If not configured, in-memory storage is used (not recommended for production)
Authentication & Security
| Variable | Required | Default | Description |
|---|---|---|---|
JWT_SECRET | ✅ | - | Secret key for signing JWT tokens (min 32 characters) |
JWT_EXPIRES_IN | ❌ | 15m | Access token expiration time |
REFRESH_TOKEN_EXPIRES_IN | ❌ | 7d | Refresh token expiration time |
BCRYPT_ROUNDS | ❌ | 12 | BCrypt hashing rounds for passwords |
Examples:
# Generate a secure JWT secret
JWT_SECRET=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6
# Token expiration
JWT_EXPIRES_IN=15m
REFRESH_TOKEN_EXPIRES_IN=7d
# Password hashing strength
BCRYPT_ROUNDS=12Generate secure JWT secret:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# or
openssl rand -hex 32CORS Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
CORS_ORIGINS | ❌ | http://localhost:3000 | Allowed frontend origins (comma-separated) |
CORS_CREDENTIALS | ❌ | true | Allow credentials in CORS requests |
Examples:
# Development
CORS_ORIGINS=http://localhost:3000,http://localhost:3001
# Production
CORS_ORIGINS=https://yourdomain.com,https://www.yourdomain.com,https://app.yourdomain.com
# Multiple environments
CORS_ORIGINS=https://staging.yourdomain.com,https://yourdomain.com,https://www.yourdomain.comRate Limiting
| Variable | Required | Default | Description |
|---|---|---|---|
RATE_LIMIT_TTL | ❌ | 60000 | Rate limit window in milliseconds |
RATE_LIMIT_MAX | ❌ | 100 | Maximum requests per window |
RATE_LIMIT_AUTH_TTL | ❌ | 60000 | Auth endpoint rate limit window |
RATE_LIMIT_AUTH_MAX | ❌ | 10 | Auth endpoint max requests per window |
Examples:
# Development (relaxed)
RATE_LIMIT_TTL=60000 # 1 minute
RATE_LIMIT_MAX=1000 # 1000 requests per minute
RATE_LIMIT_AUTH_TTL=60000
RATE_LIMIT_AUTH_MAX=20 # 20 auth attempts per minute
# Production (strict)
RATE_LIMIT_TTL=60000 # 1 minute
RATE_LIMIT_MAX=100 # 100 requests per minute
RATE_LIMIT_AUTH_TTL=60000
RATE_LIMIT_AUTH_MAX=5 # 5 auth attempts per minuteStripe Integration
| Variable | Required | Default | Description |
|---|---|---|---|
STRIPE_SECRET_KEY | ✅ | - | Stripe secret API key |
STRIPE_WEBHOOK_SECRET | ✅ | - | Stripe webhook endpoint secret |
STRIPE_WEBHOOK_TOLERANCE | ❌ | 300 | Webhook timestamp tolerance in seconds |
Examples:
# Development (test keys)
STRIPE_SECRET_KEY=sk_test_51ABC123...
STRIPE_WEBHOOK_SECRET=whsec_abc123...
# Production (live keys)
STRIPE_SECRET_KEY=sk_live_51XYZ789...
STRIPE_WEBHOOK_SECRET=whsec_xyz789...Getting Stripe keys:
- API Keys: Stripe Dashboard → Developers → API Keys
- Webhook Secret: Stripe Dashboard → Developers → Webhooks → Select endpoint → Reveal signing secret
Email Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
EMAIL_HOST | ❌ | - | SMTP server hostname |
EMAIL_PORT | ❌ | 587 | SMTP server port |
EMAIL_USER | ❌ | - | SMTP username |
EMAIL_PASSWORD | ❌ | - | SMTP password |
EMAIL_FROM | ❌ | noreply@localhost | From email address |
EMAIL_FROM_NAME | ❌ | ShopySeed | From name |
Examples:
# Resend (recommended)
EMAIL_HOST=smtp.resend.com
EMAIL_PORT=587
EMAIL_USER=resend
EMAIL_PASSWORD=re_abc123...
EMAIL_FROM=noreply@yourdomain.com
EMAIL_FROM_NAME="Your SaaS Name"
# SendGrid
EMAIL_HOST=smtp.sendgrid.net
EMAIL_PORT=587
EMAIL_USER=apikey
EMAIL_PASSWORD=SG.abc123...
EMAIL_FROM=noreply@yourdomain.com
EMAIL_FROM_NAME="Your SaaS Name"
# Amazon SES
EMAIL_HOST=email-smtp.us-east-1.amazonaws.com
EMAIL_PORT=587
EMAIL_USER=AKIA...
EMAIL_PASSWORD=BHs5...
EMAIL_FROM=noreply@yourdomain.com
EMAIL_FROM_NAME="Your SaaS Name"
# Development (optional - uses MailHog if not configured)
# Leave empty to use MailHog for testingOAuth Providers
| Variable | Required | Default | Description |
|---|---|---|---|
GOOGLE_CLIENT_ID | ❌ | - | Google OAuth client ID |
GOOGLE_CLIENT_SECRET | ❌ | - | Google OAuth client secret |
GITHUB_CLIENT_ID | ❌ | - | GitHub OAuth app ID |
GITHUB_CLIENT_SECRET | ❌ | - | GitHub OAuth app secret |
OAUTH_REDIRECT_URL | ❌ | http://localhost:3000/auth/callback | OAuth callback URL |
Examples:
# Google OAuth
GOOGLE_CLIENT_ID=123456789-abc123.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-abc123...
# GitHub OAuth
GITHUB_CLIENT_ID=abc123...
GITHUB_CLIENT_SECRET=github_pat_123...
# Callback URL (must match OAuth app settings)
OAUTH_REDIRECT_URL=https://yourdomain.com/auth/callbackSetting up OAuth:
- Google: Google Cloud Console → APIs & Services → Credentials
- GitHub: GitHub Settings → OAuth Apps
File Upload & Storage
| Variable | Required | Default | Description |
|---|---|---|---|
UPLOAD_MAX_SIZE | ❌ | 10485760 | Maximum file size in bytes (10MB) |
UPLOAD_ALLOWED_TYPES | ❌ | image/jpeg,image/png,image/gif | Allowed MIME types (comma-separated) |
AWS_ACCESS_KEY_ID | ❌ | - | AWS access key for S3 uploads |
AWS_SECRET_ACCESS_KEY | ❌ | - | AWS secret key for S3 uploads |
AWS_REGION | ❌ | us-east-1 | AWS region for S3 |
AWS_S3_BUCKET | ❌ | - | S3 bucket name for file uploads |
Examples:
# Local file storage (development)
UPLOAD_MAX_SIZE=10485760 # 10MB
UPLOAD_ALLOWED_TYPES=image/jpeg,image/png,image/gif,application/pdf
# S3 storage (production)
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=abc123...
AWS_REGION=us-east-1
AWS_S3_BUCKET=your-app-uploadsFrontend Web Variables (apps/web/.env.local)
API Connection
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_API_URL | ✅ | http://localhost:3011 | Backend API base URL |
NEXT_PUBLIC_APP_URL | ❌ | http://localhost:3000 | Frontend app base URL |
Examples:
# Development
NEXT_PUBLIC_API_URL=http://localhost:3011
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Production
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
NEXT_PUBLIC_APP_URL=https://yourdomain.com
# Staging
NEXT_PUBLIC_API_URL=https://api-staging.yourdomain.com
NEXT_PUBLIC_APP_URL=https://staging.yourdomain.comStripe Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | ✅ | - | Stripe publishable key |
Examples:
# Development
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51ABC123...
# Production
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_51XYZ789...OAuth URLs
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_GOOGLE_OAUTH_URL | ❌ | - | Google OAuth initiation URL |
NEXT_PUBLIC_GITHUB_OAUTH_URL | ❌ | - | GitHub OAuth initiation URL |
Examples:
# Development
NEXT_PUBLIC_GOOGLE_OAUTH_URL=http://localhost:3011/auth/oauth/google
NEXT_PUBLIC_GITHUB_OAUTH_URL=http://localhost:3011/auth/oauth/github
# Production
NEXT_PUBLIC_GOOGLE_OAUTH_URL=https://api.yourdomain.com/auth/oauth/google
NEXT_PUBLIC_GITHUB_OAUTH_URL=https://api.yourdomain.com/auth/oauth/githubAnalytics & Tracking
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_GA_ID | ❌ | - | Google Analytics tracking ID |
NEXT_PUBLIC_POSTHOG_KEY | ❌ | - | PostHog project API key |
NEXT_PUBLIC_SENTRY_DSN | ❌ | - | Sentry error tracking DSN |
Examples:
# Google Analytics
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX
# PostHog
NEXT_PUBLIC_POSTHOG_KEY=phc_abc123...
# Sentry
NEXT_PUBLIC_SENTRY_DSN=https://abc123@sentry.io/123456Global Production Variables (.env.production)
Docker Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
API_PORT | ❌ | 3011 | Exposed API port |
WEB_PORT | ❌ | 3000 | Exposed web port |
POSTGRES_PORT | ❌ | 5433 | Exposed PostgreSQL port |
REDIS_PORT | ❌ | 6379 | Exposed Redis port |
Examples:
# Standard ports
API_PORT=3011
WEB_PORT=3000
POSTGRES_PORT=5433
REDIS_PORT=6379
# Custom ports (if conflicts exist)
API_PORT=8011
WEB_PORT=8000
POSTGRES_PORT=5434
REDIS_PORT=6380Monitoring & Observability
| Variable | Required | Default | Description |
|---|---|---|---|
GRAFANA_PASSWORD | ❌ | - | Grafana admin password |
PROMETHEUS_RETENTION | ❌ | 15d | Prometheus data retention period |
Examples:
# Monitoring
GRAFANA_PASSWORD=secure_grafana_password
PROMETHEUS_RETENTION=30dEnvironment-Specific Examples
Complete Development Configuration
apps/api/.env
# Application
NODE_ENV=development
PORT=3011
LOG_LEVEL=debug
# Database
DATABASE_URL=postgresql://postgres:postgres@localhost:5433/shopyseed_dev?schema=public
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=shopyseed_dev
POSTGRES_PORT=5433
# Redis
REDIS_URL=redis://localhost:6379
# Security (development values)
JWT_SECRET=development-jwt-secret-minimum-32-characters
CORS_ORIGINS=http://localhost:3000
# Rate limiting (relaxed for development)
RATE_LIMIT_TTL=60000
RATE_LIMIT_MAX=1000
RATE_LIMIT_AUTH_MAX=20
# Stripe (test keys)
STRIPE_SECRET_KEY=sk_test_your_test_key
STRIPE_WEBHOOK_SECRET=whsec_your_test_webhook_secret
# Email (optional - uses MailHog if not set)
# EMAIL_HOST=smtp.resend.com
# EMAIL_PORT=587
# EMAIL_USER=resend
# EMAIL_PASSWORD=your_test_api_key
# EMAIL_FROM=dev@localhost
# OAuth (optional)
# GOOGLE_CLIENT_ID=your-google-client-id
# GOOGLE_CLIENT_SECRET=your-google-client-secretapps/web/.env.local
# API Connection
NEXT_PUBLIC_API_URL=http://localhost:3011
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_your_test_publishable_key
# OAuth
NEXT_PUBLIC_GOOGLE_OAUTH_URL=http://localhost:3011/auth/oauth/google
NEXT_PUBLIC_GITHUB_OAUTH_URL=http://localhost:3011/auth/oauth/githubComplete Production Configuration
apps/api/.env.production
# Application
NODE_ENV=production
PORT=3011
LOG_LEVEL=info
# Database
DATABASE_URL=postgresql://postgres:STRONG_DB_PASSWORD@postgres:5432/shopyseed_prod?schema=public
POSTGRES_USER=postgres
POSTGRES_PASSWORD=STRONG_DB_PASSWORD
POSTGRES_DB=shopyseed_prod
# Redis
REDIS_URL=redis://redis:6379
# Security
JWT_SECRET=SECURE_32_CHAR_JWT_SECRET_FOR_PRODUCTION
CORS_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
# Rate limiting
RATE_LIMIT_TTL=60000
RATE_LIMIT_MAX=100
RATE_LIMIT_AUTH_MAX=5
# Stripe (live keys)
STRIPE_SECRET_KEY=sk_live_your_live_secret_key
STRIPE_WEBHOOK_SECRET=whsec_your_live_webhook_secret
# Email
EMAIL_HOST=smtp.resend.com
EMAIL_PORT=587
EMAIL_USER=resend
EMAIL_PASSWORD=your_production_api_key
EMAIL_FROM=noreply@yourdomain.com
EMAIL_FROM_NAME="Your SaaS Name"
# OAuth
GOOGLE_CLIENT_ID=your-production-google-client-id
GOOGLE_CLIENT_SECRET=your-production-google-client-secret
GITHUB_CLIENT_ID=your-production-github-client-id
GITHUB_CLIENT_SECRET=your-production-github-client-secret
# File uploads
AWS_ACCESS_KEY_ID=your-aws-access-key
AWS_SECRET_ACCESS_KEY=your-aws-secret-key
AWS_REGION=us-east-1
AWS_S3_BUCKET=your-production-uploads-bucketapps/web/.env.production
# API Connection
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
NEXT_PUBLIC_APP_URL=https://yourdomain.com
# Stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_your_live_publishable_key
# OAuth
NEXT_PUBLIC_GOOGLE_OAUTH_URL=https://api.yourdomain.com/auth/oauth/google
NEXT_PUBLIC_GITHUB_OAUTH_URL=https://api.yourdomain.com/auth/oauth/github
# Analytics
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX
NEXT_PUBLIC_SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-idSecurity Considerations
Environment File Security
-
Never commit environment files to git
# .gitignore should include: .env* !.env.example !.env*.example -
Use strong, unique values in production
- JWT secrets: minimum 32 characters, cryptographically random
- Database passwords: long, complex passwords
- API keys: use production-grade keys from providers
-
Restrict access to environment files
chmod 600 .env*
Secret Management
For production deployments, consider using dedicated secret management:
AWS Secrets Manager
# Store secrets in AWS Secrets Manager
aws secretsmanager create-secret \
--name "shopyseed/production/jwt-secret" \
--secret-string "your-secure-jwt-secret"
# Retrieve in application
JWT_SECRET=$(aws secretsmanager get-secret-value \
--secret-id "shopyseed/production/jwt-secret" \
--query SecretString --output text)Docker Secrets
# docker-compose.prod.yml
secrets:
jwt_secret:
external: true
db_password:
external: true
services:
api:
secrets:
- jwt_secret
- db_password
environment:
JWT_SECRET_FILE: /run/secrets/jwt_secret
POSTGRES_PASSWORD_FILE: /run/secrets/db_passwordKubernetes Secrets
apiVersion: v1
kind: Secret
metadata:
name: shopyseed-secrets
type: Opaque
data:
jwt-secret: <base64-encoded-secret>
db-password: <base64-encoded-password>Validation
Environment Validation Schema
ShopySeed includes runtime environment validation. Add new variables to the validation schema:
// apps/api/src/common/config/env.validation.ts
import { plainToClass, Type } from 'class-transformer';
import { IsString, IsNumber, IsOptional, IsBoolean, Min, Max } from 'class-validator';
export class EnvironmentVariables {
@IsString()
NODE_ENV: string;
@Type(() => Number)
@IsNumber()
@Min(1)
@Max(65535)
@IsOptional()
PORT?: number = 3011;
@IsString()
DATABASE_URL: string;
@IsString()
JWT_SECRET: string;
// Add validation for your custom environment variables
@IsString()
@IsOptional()
YOUR_CUSTOM_VARIABLE?: string;
@Type(() => Number)
@IsNumber()
@IsOptional()
YOUR_NUMERIC_VARIABLE?: number;
@Type(() => Boolean)
@IsBoolean()
@IsOptional()
YOUR_BOOLEAN_VARIABLE?: boolean = false;
}
export function validate(config: Record<string, unknown>) {
const validatedConfig = plainToClass(EnvironmentVariables, config, {
enableImplicitConversion: true,
});
return validatedConfig;
}Manual Validation
You can also validate your environment setup:
# Check required variables
node -e "
const required = ['DATABASE_URL', 'JWT_SECRET', 'STRIPE_SECRET_KEY'];
const missing = required.filter(key => !process.env[key]);
if (missing.length) {
console.error('Missing required environment variables:', missing);
process.exit(1);
} else {
console.log('All required environment variables are set ✅');
}
"Troubleshooting
Common Issues
1. Database Connection Fails
# Test connection manually
psql postgresql://postgres:password@localhost:5433/shopyseed_dev
# Check if DATABASE_URL is properly formatted
node -e "console.log(process.env.DATABASE_URL)"2. CORS Errors
# Verify CORS_ORIGINS includes your frontend URL
# Development: http://localhost:3000
# Production: https://yourdomain.com3. Stripe Webhooks Fail
# Test webhook secret
stripe listen --forward-to localhost:3011/billing/webhooks4. JWT Token Issues
# Verify JWT secret length (minimum 32 characters)
node -e "console.log('JWT_SECRET length:', process.env.JWT_SECRET?.length)"5. Email Not Sending
# Test SMTP connection
node -e "
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransporter({
host: process.env.EMAIL_HOST,
port: process.env.EMAIL_PORT,
auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASSWORD }
});
transporter.verify().then(() => console.log('SMTP connection successful'))
.catch(err => console.error('SMTP connection failed:', err));
"Quick Reference
Development Setup Checklist
- Copy
.env.examplefiles to.envfiles - Set
DATABASE_URLfor local PostgreSQL - Generate
JWT_SECRET(32+ characters) - Configure
STRIPE_SECRET_KEYandSTRIPE_WEBHOOK_SECRET(test keys) - Set
CORS_ORIGINS=http://localhost:3000 - Set
NEXT_PUBLIC_API_URL=http://localhost:3011 - Set
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY(test key)
Production Setup Checklist
- Use strong, unique passwords and secrets
- Configure production database connection
- Set up production Stripe keys and webhooks
- Configure email provider (Resend, SendGrid, etc.)
- Set production CORS origins
- Configure OAuth providers with production URLs
- Set up file storage (S3, etc.)
- Configure monitoring and analytics
- Secure environment files (chmod 600)
- Use secret management service for sensitive data
This environment reference ensures your ShopySeed deployment is properly configured and secure across all environments. 🚀