| name | setup-env |
| description | Environment variable setup patterns. Core file covers Node.js (dotenv, NEXT_PUBLIC_, VITE_). Runtime files: python.md (pydantic-settings, Django settings), dotnet.md (appsettings.json, User Secrets), go.md (caarlos0/env, godotenv). |
Environment Setup Tool
Generate production-ready environment variable files with all configuration extracted from your architecture blueprint.
Runtime-specific patterns: For non-Node.js backends, read the matching file:
- Python/FastAPI/Django:
skills/setup-env/python.md
- .NET:
skills/setup-env/dotnet.md
- Go:
skills/setup-env/go.md
Perfect for: New project setup, developer onboarding, deployment configuration, secrets management
When to Use This Skill
Use this skill when you need to:
- Set up environment variables after running
/architect:scaffold
- Onboard new developers with correct configuration
- Prepare for deployment (staging, production environments)
- Document all required API keys and secrets
- Validate environment configuration before running the app
- Generate Docker/Kubernetes config maps
Input: Architecture blueprint (extracts from Tech Stack, Integrations, Security sections)
Output: .env.example, .env.local, validate-env.sh
Files Generated
1. .env.example
Purpose: Template committed to git, no secrets
Content: All required environment variables with placeholder values and comments
DATABASE_URL="postgresql://user:password@localhost:5432/dbname"
DATABASE_POOL_SIZE=10
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_xxx"
CLERK_SECRET_KEY="sk_test_xxx"
R2_ACCOUNT_ID="your-account-id"
R2_ACCESS_KEY_ID="your-access-key"
R2_SECRET_ACCESS_KEY="your-secret-key"
R2_BUCKET_NAME="your-bucket-name"
RESEND_API_KEY="re_xxx"
RESEND_FROM_EMAIL="noreply@yourdomain.com"
SLACK_WEBHOOK_URL="https://hooks.slack.com/services/xxx"
STRIPE_PUBLISHABLE_KEY="pk_test_xxx"
STRIPE_SECRET_KEY="sk_test_xxx"
STRIPE_WEBHOOK_SECRET="whsec_xxx"
NEXT_PUBLIC_APP_URL="http://localhost:3000"
NODE_ENV="development"
LOG_LEVEL="debug"
2. .env.local
Purpose: Local development secrets, NOT committed to git
Content: Same variables with actual values (or instructions to fill in)
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/myapp_dev"
DATABASE_POOL_SIZE=10
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="<GET_FROM_CLERK_DASHBOARD>"
CLERK_SECRET_KEY="<GET_FROM_CLERK_DASHBOARD>"
R2_ACCOUNT_ID="<GET_FROM_CLOUDFLARE>"
R2_ACCESS_KEY_ID="<GET_FROM_CLOUDFLARE>"
R2_SECRET_ACCESS_KEY="<GET_FROM_CLOUDFLARE>"
R2_BUCKET_NAME="myapp-dev"
[... all variables with TODO or default values ...]
3. validate-env.sh
Purpose: Validate all required environment variables are set
Content: Shell script that checks each variable and reports missing ones
#!/bin/bash
set -e
MISSING=()
check_var() {
VAR_NAME=$1
VAR_VALUE=${!VAR_NAME}
if [ -z "$VAR_VALUE" ] || [[ "$VAR_VALUE" == "<GET_FROM_"* ]]; then
MISSING+=("$VAR_NAME")
fi
}
echo "🔍 Validating environment variables..."
check_var "DATABASE_URL"
check_var "DATABASE_POOL_SIZE"
check_var "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY"
check_var "CLERK_SECRET_KEY"
check_var "R2_ACCOUNT_ID"
check_var "R2_ACCESS_KEY_ID"
check_var "R2_SECRET_ACCESS_KEY"
check_var "R2_BUCKET_NAME"
[... check all variables ...]
if [ ${#MISSING[@]} -eq 0 ]; then
echo "✅ All environment variables are set!"
exit 0
else
echo "❌ Missing or incomplete environment variables:"
for var in "${MISSING[@]}"; do
echo " - $var"
done
echo ""
echo "See .env.example for details on how to obtain these values."
exit 1
fi
4. .env.production.example (optional)
Purpose: Production environment template
Content: Production-specific variables (different URLs, higher limits)
DATABASE_URL="<PRODUCTION_DATABASE_URL>"
DATABASE_POOL_SIZE=50
NEXT_PUBLIC_APP_URL="https://yourdomain.com"
NODE_ENV="production"
LOG_LEVEL="info"
ENABLE_ANALYTICS=true
ENABLE_ERROR_TRACKING=true
SENTRY_DSN="<SENTRY_DSN>"
Multi-Service Projects — Per-Service .env Scoping
For projects with multiple backend services, each service gets its own .env containing only the variables it actually uses, derived from architecture.services[].dependsOn[] in the SDL.
Step 0: Read dependsOn[] from SDL
Before generating any .env files, read architecture.services[] from the SDL. Check solution.sdl.yaml first; if absent, read sdl/README.md then the relevant module (typically sdl/architecture.yaml or sdl/services.yaml):
architecture:
services:
- name: api-server
dependsOn: [stripe, sendgrid, postgres]
- name: worker
dependsOn: [postgres, redis, sendgrid]
- name: web-app
dependsOn: [api-server]
Build a per-service variable map:
| Service | Gets these variable groups |
|---|
api-server | DATABASE_URL, STRIPE_, SENDGRID_ |
worker | DATABASE_URL, REDIS_URL, SENDGRID_* |
web-app | NEXT_PUBLIC_API_URL (pointing to api-server) |
Rules:
- A service that
dependsOn: [stripe] gets STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET
- A service that
dependsOn: [another-service] gets <SERVICE_NAME>_URL pointing to that sibling
- Shared infrastructure vars (
DATABASE_URL, REDIS_URL) only go to services that declare that dependency
- Never write Stripe keys into a service that doesn't call Stripe
- If
dependsOn[] is absent from SDL, fall back to a single shared .env for all services
Per-Service Output Structure
<project-root>/
├── api-server/
│ ├── .env.example ← only api-server vars
│ └── .env.local
├── worker/
│ ├── .env.example ← only worker vars
│ └── .env.local
└── web-app/
├── .env.example ← only web-app vars (NEXT_PUBLIC_*)
└── .env.local
Shared secrets (e.g. DATABASE_URL) appear in each service that needs them — ask the user for the value once and replicate to all relevant .env files.
How It Works
Step 1: Extract Variables from Blueprint
Scan blueprint sections for environment variable requirements:
From Section 3: Tech Stack Decisions
- Database:
DATABASE_URL, DATABASE_POOL_SIZE
- Caching (if Redis):
REDIS_URL, REDIS_PASSWORD
From Section 6: API Specification
NEXT_PUBLIC_APP_URL
API_RATE_LIMIT_PER_MINUTE
API_TIMEOUT_MS
From Section 7: Integrations
For each integration detected:
- Stripe:
STRIPE_PUBLISHABLE_KEY, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET
- Clerk:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY
- Resend:
RESEND_API_KEY, RESEND_FROM_EMAIL
- Slack:
SLACK_WEBHOOK_URL, SLACK_BOT_TOKEN
- OpenAI:
OPENAI_API_KEY, OPENAI_ORG_ID
- Cloudflare R2:
R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET_NAME
- AWS S3:
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, S3_BUCKET_NAME
- GCS:
GCS_PROJECT_ID, GCS_BUCKET_NAME, GOOGLE_APPLICATION_CREDENTIALS
- Azure Blob:
AZURE_STORAGE_CONNECTION_STRING, AZURE_STORAGE_CONTAINER
From Data: Queues
- RabbitMQ:
RABBITMQ_URL
- SQS:
AWS_SQS_QUEUE_URL, AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
- Kafka:
KAFKA_BROKERS, KAFKA_CLIENT_ID, KAFKA_SASL_USERNAME, KAFKA_SASL_PASSWORD
- Azure Service Bus:
AZURE_SERVICE_BUS_CONNECTION_STRING
- Redis (queue):
REDIS_QUEUE_URL
From Data: Search
- Elasticsearch:
ELASTICSEARCH_URL, ELASTICSEARCH_API_KEY
- Algolia:
ALGOLIA_APP_ID, ALGOLIA_API_KEY, ALGOLIA_SEARCH_KEY
- Typesense:
TYPESENSE_URL, TYPESENSE_API_KEY
- Meilisearch:
MEILISEARCH_URL, MEILISEARCH_API_KEY
- Azure Search:
AZURE_SEARCH_ENDPOINT, AZURE_SEARCH_API_KEY
- Pinecone:
PINECONE_API_KEY, PINECONE_ENVIRONMENT, PINECONE_INDEX
- Qdrant:
QDRANT_URL, QDRANT_API_KEY
- Weaviate:
WEAVIATE_URL, WEAVIATE_API_KEY
From Section 8: Security Architecture
JWT_SECRET or SESSION_SECRET
ENCRYPTION_KEY (if encryption used)
CORS_ALLOWED_ORIGINS
From Section 9: Deployment & DevOps
NODE_ENV
LOG_LEVEL
ENABLE_DEBUG
- Monitoring:
SENTRY_DSN, DATADOG_API_KEY
From Product Type Detection
- Multi-tenant:
DEFAULT_TENANT_ID, TENANT_ISOLATION_MODE
- E-commerce: Payment provider variables
- AI agents: LLM provider variables
- Real-time: WebSocket/SSE configuration
Step 2: Categorize Variables
Group variables by category for organization:
DATABASE_URL="..."
DATABASE_POOL_SIZE=10
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="..."
CLERK_SECRET_KEY="..."
[...]
Step 3: Add Comments and Links
For each variable, add:
- Description: What this variable controls
- Required: Yes/No
- Where to get it: Dashboard URL or signup link
- Example value: Placeholder showing expected format
- Security level: Public (NEXT_PUBLIC_*), Secret, Internal
Example:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_xxx"
CLERK_SECRET_KEY="sk_test_xxx"
Step 4: Set Default Values
Where applicable, provide sensible defaults:
DATABASE_POOL_SIZE=10
API_RATE_LIMIT_PER_MINUTE=60
API_TIMEOUT_MS=30000
LOG_LEVEL="debug"
NODE_ENV="development"
NEXT_PUBLIC_APP_URL="http://localhost:3000"
Step 5: Generate Validation Script
Create validate-env.sh that:
- Sources
.env.local or current environment
- Checks each required variable is set
- Checks variables don't have placeholder values (
<GET_FROM_...>)
- Validates format for specific variables (URLs, keys)
- Reports all missing/invalid variables at once
- Exits with code 0 (success) or 1 (failure)
Variable format validation:
if [[ ! "$DATABASE_URL" =~ ^postgresql:// ]]; then
echo "⚠️ DATABASE_URL must start with postgresql://"
fi
if [[ ! "$NEXT_PUBLIC_APP_URL" =~ ^https?:// ]]; then
echo "⚠️ NEXT_PUBLIC_APP_URL must start with http:// or https://"
fi
if [[ ! "$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY" =~ ^pk_ ]]; then
echo "⚠️ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY must start with pk_"
fi
Step 6: Check .gitignore
Ensure .gitignore includes:
# Environment files with secrets
.env.local
.env.*.local
.env.production
# Keep template in git
!.env.example
If .gitignore doesn't exist or is missing these entries, add them automatically.
Output Format
When invoked, generate:
🔧 Generating environment configuration...
✅ Extracted 23 environment variables from blueprint
- Database: 2 variables
- Authentication (Clerk): 2 variables
- File Storage (R2): 4 variables
- Email (Resend): 2 variables
- Integrations (Stripe, Slack): 5 variables
- App Configuration: 5 variables
- Monitoring (Sentry): 1 variable
- Security: 2 variables
✅ Created .env.example (template, safe to commit)
✅ Created .env.local (with TODOs, DO NOT commit)
✅ Created validate-env.sh (validation script)
✅ Updated .gitignore to exclude .env.local
📋 Next steps to configure your environment:
1. Sign up for required services:
- Clerk: https://clerk.com (authentication)
- Cloudflare: https://cloudflare.com (file storage)
- Resend: https://resend.com (email)
- Stripe: https://stripe.com (payments)
- Sentry: https://sentry.io (error tracking)
2. Get your API keys from each dashboard
3. Fill in .env.local with your actual keys
4. Validate your configuration:
chmod +x validate-env.sh
./validate-env.sh
5. Start development:
npm run dev
🔒 Security reminders:
- NEVER commit .env.local to git
- Rotate secrets if accidentally exposed
- Use different keys for dev/staging/production
Customization Options
Optional parameters (ask user if they want to customize):
- Environment type: Development (default), Staging, Production
- Include optional variables: Yes/No (analytics, monitoring, etc.)
- Framework-specific: Next.js (default), Remix, Astro, Node.js
- Output format: Bash (default), Docker Compose, Kubernetes ConfigMap
- Validation strictness: Basic (default), Strict (validate formats)
Default behavior: Development environment, Next.js format, all variables, basic validation.
Framework-Specific Variables
Next.js
NEXT_PUBLIC_APP_URL="http://localhost:3000"
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_xxx"
CLERK_SECRET_KEY="sk_test_xxx"
DATABASE_URL="postgresql://..."
Remix
SESSION_SECRET="your-session-secret-min-32-chars"
DATABASE_URL="postgresql://..."
APP_URL="http://localhost:3000"
Astro
PUBLIC_APP_URL="http://localhost:3000"
PUBLIC_CLERK_KEY="pk_test_xxx"
CLERK_SECRET_KEY="sk_test_xxx"
Node.js / Express
PORT=3000
NODE_ENV="development"
DATABASE_URL="postgresql://..."
JWT_SECRET="your-jwt-secret"
Docker Compose Integration
Generate docker-compose.yml environment section:
version: '3.8'
services:
app:
image: myapp:latest
env_file:
- .env.local
environment:
DATABASE_URL: postgresql://db:5432/myapp
REDIS_URL: redis://redis:6379
depends_on:
- db
- redis
db:
image: postgres:15
environment:
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres}
redis:
image: redis:7-alpine
Kubernetes ConfigMap/Secret
Generate Kubernetes manifests:
configmap.yaml (non-sensitive values):
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
NODE_ENV: "production"
LOG_LEVEL: "info"
DATABASE_POOL_SIZE: "50"
NEXT_PUBLIC_APP_URL: "https://myapp.com"
secret.yaml (sensitive values):
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
type: Opaque
stringData:
DATABASE_URL: "<BASE64_ENCODED>"
CLERK_SECRET_KEY: "<BASE64_ENCODED>"
STRIPE_SECRET_KEY: "<BASE64_ENCODED>"
JWT_SECRET: "<BASE64_ENCODED>"
Validation Features
Basic Validation (Default)
- Check all required variables are set
- Check no placeholder values remain (
<GET_FROM_...>)
- Report missing variables
Strict Validation (Optional)
- Validate URL formats (http://, https://)
- Validate key prefixes (pk_, sk_, re_, etc.)
- Validate numeric values (pool size > 0)
- Validate enum values (NODE_ENV in [development, staging, production])
- Validate JWT_SECRET length (min 32 characters)
- Check for common security issues (weak secrets, exposed tokens)
Example strict validation:
if [ ${#JWT_SECRET} -lt 32 ]; then
echo "❌ JWT_SECRET must be at least 32 characters long"
echo " Current length: ${#JWT_SECRET}"
echo " Generate a strong secret: openssl rand -base64 48"
fi
if [[ "$CORS_ALLOWED_ORIGINS" == "*" ]] && [[ "$NODE_ENV" == "production" ]]; then
echo "❌ CORS_ALLOWED_ORIGINS='*' is dangerous in production"
echo " Use specific origins instead"
fi
Security Best Practices
Included in Generated Files
- Clear secret markings:
CLERK_SECRET_KEY="sk_test_xxx"
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_xxx"
- Rotation reminders:
JWT_SECRET="..."
- Environment separation:
STRIPE_SECRET_KEY="sk_test_xxx"
- Git commit prevention:
.env.local
.env.*.local
.env.production
git diff --cached --name-only | grep -E "\.env\.local"
Error Handling
If blueprint section missing:
- Action: Use defaults for that category
- Notify user: "ℹ️ No integrations detected, skipping Stripe/Slack variables"
If .gitignore already has .env entries:
- Action: Skip adding duplicates
- Notify user: "✅ .gitignore already configured for environment files"
If .env.local already exists:
- Action: Ask user before overwriting
- Options: "1) Backup and overwrite, 2) Merge new variables, 3) Cancel"
If validation script fails:
Integration with Other Skills
Recommended workflow:
/architect:blueprint
/architect:scaffold
/architect:setup-env
chmod +x validate-env.sh
./validate-env.sh
npm run dev
Success Criteria
A successful environment setup should:
- ✅ Include all variables required by the blueprint
- ✅ Categorize variables logically (Database, Auth, etc.)
- ✅ Provide clear comments and links for each variable
- ✅ Include sensible defaults where applicable
- ✅ Create validation script that catches missing values
- ✅ Update .gitignore to prevent secret leakage
- ✅ Distinguish public vs secret variables clearly
- ✅ Support both development and production environments
- ✅ Be framework-aware (Next.js, Remix, etc.)
- ✅ Pass validation script on first run (with TODOs)
Examples
Example 1: Basic Setup
/architect:setup-env
Example 2: Production Environment
/architect:setup-env --env=production
Example 3: Docker Compose
/architect:setup-env --format=docker
Example 4: Kubernetes
/architect:setup-env --format=kubernetes