Skip to content

Environment Variable Management Guide

Environment Variable Management Guide

This guide explains how to manage environment variables across all Seed & Source templates.

Overview

Environment variables are used to configure applications without hardcoding values. This is essential for:

  • Separating configuration from code
  • Supporting multiple environments (dev, staging, production)
  • Protecting sensitive data (API keys, secrets)
  • Enabling feature flags

Validation Scripts

Each template includes a validation script to check environment variables:

Python SaaS

Terminal window
python bin/validate_env.py # Validate with warnings
python bin/validate_env.py --strict # Exit with error if missing
python bin/validate_env.py --help # Show detailed help

Rails API

Terminal window
ruby bin/validate_env.rb # Validate with warnings
ruby bin/validate_env.rb --strict # Exit with error if missing
ruby bin/validate_env.rb --help # Show detailed help

React Client

Terminal window
node bin/validate_env.js # Validate with warnings
node bin/validate_env.js --strict # Exit with error if missing
node bin/validate_env.js --help # Show detailed help

Static Landing (Astro)

Terminal window
node bin/validate_env.js # Validate with warnings
node bin/validate_env.js --strict # Exit with error if missing
node bin/validate_env.js --help # Show detailed help

Environment Files

File Priority (highest to lowest)

  1. .env.local - Local overrides (gitignored, highest priority)
  2. .env.production - Production-specific values
  3. .env.development - Development-specific values
  4. .env - Default values
  5. .env.example - Template/documentation (committed to git)

Best Practices

  • DO: Commit .env.example files
  • DO: Use .env.local for local development
  • DON’T: Commit .env, .env.local, or .env.production
  • DON’T: Store secrets in git

Template-Specific Requirements

Python SaaS

Required Variables:

  • APP_ENV - Application environment (development, production)
  • DATABASE_URL - Database connection string
  • SECRET_KEY - Secret key for cryptographic operations

Recommended Variables:

  • OPENAI_API_KEY - For AI features
  • REDIS_URL - For caching
  • SENTRY_DSN - For error tracking

Example:

Terminal window
# Generate SECRET_KEY
openssl rand -hex 32
# Set environment variables
export SECRET_KEY=your_generated_secret
export DATABASE_URL=postgresql://user:pass@localhost/db

Rails API

Required Variables:

  • SECRET_KEY_BASE - Rails secret key base
  • DATABASE_URL - PostgreSQL connection string
  • REDIS_URL - Redis connection string
  • ALLOWED_ORIGINS - CORS allowed origins

Recommended Variables:

  • JWT_SECRET - For JWT authentication
  • LICENSE_SERVER_URL - License server integration
  • SENTRY_DSN - Error tracking

Example:

Terminal window
# Generate SECRET_KEY_BASE
bundle exec rails secret
# Generate JWT_SECRET
openssl rand -hex 32
# Set environment variables
export SECRET_KEY_BASE=your_generated_secret
export DATABASE_URL=postgresql://user:pass@localhost/db
export ALLOWED_ORIGINS=https://app.example.com

React Client (Vite)

Important: Only variables prefixed with VITE_ are exposed to the client!

Required Variables:

  • VITE_BACKEND_URL - Backend API base URL
  • VITE_API_ROOT - API root path (e.g., /api/v1)

Recommended Variables:

  • VITE_SENTRY_DSN - Error tracking
  • VITE_LICENSE_SERVER_URL - License server URL

Example:

.env.production
VITE_BACKEND_URL=https://api.example.com
VITE_API_ROOT=/api/v1
VITE_SENTRY_DSN=https://...@sentry.io/...

Access in Code:

const apiUrl = import.meta.env.VITE_BACKEND_URL;

Static Landing (Astro)

Important: Only variables prefixed with PUBLIC_ are exposed to the client!

Required Variables:

  • PUBLIC_SITE_URL - Base URL of the website
  • PUBLIC_SITE_NAME - Name of the website
  • PUBLIC_CONTACT_EMAIL - Contact email address

Optional Variables:

  • PUBLIC_ANALYTICS_ID - Analytics tracking ID
  • PUBLIC_ENABLE_ANALYTICS - Enable analytics
  • PUBLIC_API_URL - API endpoint if needed

Example:

.env.production
PUBLIC_SITE_URL=https://seedsource.dev
PUBLIC_SITE_NAME=Seed & Source
PUBLIC_CONTACT_EMAIL=hello@seedsource.dev
PUBLIC_ENABLE_ANALYTICS=true
PUBLIC_ANALYTICS_ID=G-XXXXXXXXXX

Access in Code:

const siteUrl = import.meta.env.PUBLIC_SITE_URL;

Docker Compose Integration

Environment variables can be set in docker-compose.yml:

services:
api:
env_file:
- .env
- .env.local # Override with local settings
environment:
- DATABASE_URL=${DATABASE_URL}
- SECRET_KEY_BASE=${SECRET_KEY_BASE}

Production Deployment

Pre-Deployment Checklist

  • All required variables set
  • Secrets generated (not placeholders like “changeme”)
  • URLs use HTTPS in production
  • CORS origins updated for production
  • Validation script passes (--strict mode)
  • Secrets stored in secure vault (Doppler, AWS Secrets Manager, etc.)

Platform-Specific Instructions

Render

Terminal window
# Set via Render dashboard or CLI
render config:set SECRET_KEY_BASE=xxx

Fly.io

Terminal window
# Set secrets via flyctl
flyctl secrets set SECRET_KEY_BASE=xxx

Heroku

Terminal window
# Set config vars
heroku config:set SECRET_KEY_BASE=xxx

Docker Swarm

Terminal window
# Use Docker secrets
echo "secret_value" | docker secret create secret_key_base -

Secrets Management

Terminal window
# Install Doppler CLI
brew install dopplerhq/cli/doppler # macOS
curl -Ls https://cli.doppler.com/install.sh | sh # Linux
# Login and setup
doppler login
doppler setup
# Run with Doppler
doppler run -- npm start
doppler run -- docker-compose up

AWS Secrets Manager

Terminal window
# Store secret
aws secretsmanager create-secret \
--name /myapp/secret_key_base \
--secret-string "xxx"
# Retrieve in application code

HashiCorp Vault

Terminal window
# Store secret
vault kv put secret/myapp secret_key_base=xxx
# Retrieve in application code

Troubleshooting

”Environment variable not set”

  1. Check .env file exists
  2. Verify variable name is correct (case-sensitive)
  3. Restart container: docker-compose restart
  4. Check docker-compose.yml has env_file or environment section

”CORS error” in browser

  1. Check ALLOWED_ORIGINS / CORS_ALLOWED_ORIGINS
  2. Include protocol (http/https) and port
  3. No trailing slashes in URLs
  4. Restart backend after changes

Variables not available in Vite/Astro

  1. Ensure variable has correct prefix (VITE_ or PUBLIC_)
  2. Restart dev server after changes
  3. Variables are embedded at BUILD time, not runtime
  4. Check build output for warnings

”Cannot connect to database”

  1. Check DATABASE_URL format
  2. Verify database is running: docker-compose ps postgres
  3. Test connection: psql $DATABASE_URL
  4. Check credentials are correct

Testing

Run validation tests:

Terminal window
# Python SaaS
pytest tests/test_env_validation.py
# Rails API
rspec spec/bin/validate_env_spec.rb
# React Client
npm test -- validate_env.test.js
# Static Landing
npm test -- validate_env.spec.js

Security Best Practices

  1. Never commit secrets to git

    • Add .env* to .gitignore (except .env.example)
    • Use git-secrets or similar tools to prevent accidental commits
  2. Rotate secrets regularly

    • Change secrets every 90 days
    • Rotate immediately if exposed
  3. Use strong secrets

    • Minimum 32 characters
    • Use cryptographically secure random generation
    • Don’t use dictionary words or patterns
  4. Limit access

    • Use least-privilege principle
    • Different secrets per environment
    • Audit access logs
  5. Use secrets management

    • Doppler, AWS Secrets Manager, or HashiCorp Vault
    • Never store secrets in CI/CD logs
    • Encrypt secrets at rest

References