# ============================================================================= # Django SaaS Kit — Environment variables # Copy to `.env` and adjust values: cp .env.example .env # ============================================================================= # # Legend: # [REQUIRED] — startup fails if missing (python-decouple) # [OPTIONAL] — has a safe default in settings # # ============================================================================= # --- Django core ---------------------------------------------------------------- # [REQUIRED] Cryptographic signing key — generate a unique value per environment SECRET_KEY=change-me-in-production-use-a-long-random-string # [OPTIONAL] Debug mode (local: True, production/staging: False) DEBUG=True # [REQUIRED in prod/staging] Comma-separated hostnames ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0,testserver,.localhost # [OPTIONAL] Active settings module DJANGO_SETTINGS_MODULE=config.settings.local # --- Database (PostgreSQL) ------------------------------------------------------ # [REQUIRED] PostgreSQL connection URL # Inside Docker (default): DATABASE_URL=postgres://saas_user:saas_pass@db:5432/saas_db # Outside Docker (direct local connection — use after: docker-compose up db): # DATABASE_URL=postgres://saas_user:saas_pass@localhost:5432/saas_db # PostgreSQL credentials (used by docker-compose db service and healthcheck) POSTGRES_DB=saas_db POSTGRES_USER=saas_user POSTGRES_PASSWORD=saas_pass POSTGRES_PORT=5432 # --- Redis / Celery ------------------------------------------------------------- # [REQUIRED] Redis URL for cache # Inside Docker: REDIS_URL=redis://redis:6379/0 # Outside Docker (direct local connection — use after: docker-compose up redis): # REDIS_URL=redis://localhost:6379/0 # [OPTIONAL] Celery broker / result backend (default: REDIS_URL) CELERY_BROKER_URL=redis://redis:6379/1 CELERY_RESULT_BACKEND=redis://redis:6379/2 REDIS_PORT=6379 # --- JWT ------------------------------------------------------------------------ # [OPTIONAL] JWT_ACCESS_TOKEN_LIFETIME_MINUTES=60 JWT_REFRESH_TOKEN_LIFETIME_DAYS=7 # --- Email ---------------------------------------------------------------------- # [OPTIONAL] EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend DEFAULT_FROM_EMAIL=noreply@example.com # --- Production / staging security (see docs/security/threat-model.md) ---------- # [OPTIONAL] Force HTTPS redirect (prod: True; staging overrides to False by default) SECURE_SSL_REDIRECT=False # [REQUIRED in prod] Comma-separated browser origins allowed to call the API. # In local.py defaults to http://localhost:3000,http://127.0.0.1:3000. # CORS_ALLOWED_ORIGINS=http://localhost:3000,https://app.yourdomain.com # [OPTIONAL] Allow cross-origin requests to include credentials (cookies, auth headers) # CORS_ALLOW_CREDENTIALS=False # [OPTIONAL] Override Content-Security-Policy in production # CONTENT_SECURITY_POLICY=default-src 'self'; frame-ancestors 'none' # [OPTIONAL] API rate limits (see docs/security/threat-model.md) # THROTTLE_LOGIN_RATE=5/minute # THROTTLE_USER_RATE=100/minute # THROTTLE_ANON_RATE=20/minute # THROTTLE_REGISTER_RATE=3/hour # THROTTLE_PASSWORD_RESET_RATE=5/hour # --- Docker --------------------------------------------------------------------- # [OPTIONAL] Run collectstatic on container start RUN_COLLECTSTATIC=false # --- SMTP (production email) ---------------------------------------------------- # Switch EMAIL_BACKEND and fill in your SMTP provider credentials. # EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend # EMAIL_HOST=smtp.sendgrid.net # EMAIL_PORT=587 # EMAIL_HOST_USER=apikey # EMAIL_HOST_PASSWORD=your-sendgrid-api-key # EMAIL_USE_TLS=True # EMAIL_USE_SSL=False # DEFAULT_FROM_EMAIL=noreply@yourdomain.com # [OPTIONAL] Lifetime of password-reset tokens in seconds (default: 3600 = 1 h) # PASSWORD_RESET_TIMEOUT=3600 # [OPTIONAL] Frontend base URL embedded in password-reset emails # FRONTEND_URL=https://app.yourdomain.com # --- Observability (see docs/observability.md) --------------------------------- # [OPTIONAL] Force JSON logs (True) or console (False); local/prod settings override # STRUCTLOG_JSON=False # [OPTIONAL] Sentry — omit SENTRY_DSN to disable (no error at startup) # SENTRY_DSN=https://key@o0.ingest.sentry.io/1 # SENTRY_ENVIRONMENT=local # SENTRY_RELEASE=0.1.0 # SENTRY_TRACES_SAMPLE_RATE=0.0 # PROMETHEUS_ENABLED=False # PROMETHEUS_METRICS_PATH=/metrics # --- MinIO (local S3-compatible media storage) ---------------------------------- # [OPTIONAL] Set USE_S3=True to route media files through MinIO / S3. # docker-compose sets USE_S3=True automatically for all services. USE_S3=False # MinIO credentials (match MINIO_ROOT_USER / MINIO_ROOT_PASSWORD in docker-compose) MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MINIO_BUCKET_NAME=saas-media MINIO_PORT=9000 MINIO_CONSOLE_PORT=9001 # When USE_S3=True these are passed to django-storages / boto3: AWS_ACCESS_KEY_ID=minioadmin AWS_SECRET_ACCESS_KEY=minioadmin AWS_STORAGE_BUCKET_NAME=saas-media # For local MinIO: AWS_S3_ENDPOINT_URL=http://localhost:9000 AWS_S3_CUSTOM_DOMAIN=localhost:9000/saas-media # For real S3 (comment the two lines above and set these): # AWS_S3_ENDPOINT_URL= # AWS_S3_REGION_NAME=us-east-1 # AWS_S3_CUSTOM_DOMAIN=saas-media.s3.amazonaws.com # --- Stripe (billing) ----------------------------------------------------------- # [OPTIONAL] Leave blank to disable billing features # STRIPE_SECRET_KEY=sk_test_... # STRIPE_WEBHOOK_SECRET=whsec_... # --- Gunicorn ------------------------------------------------------------------- # [OPTIONAL] Override in production to match your container's CPU count # GUNICORN_WORKERS=4 # GUNICORN_WORKER_CLASS=gthread # GUNICORN_THREADS=2 # GUNICORN_TIMEOUT=120 # --- Celery workers ------------------------------------------------------------- # [OPTIONAL] # CELERY_CONCURRENCY=4