- 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
```bash
# Database Configuration
DATABASE_URL="postgresql://user:password@localhost:5432/dbname"
DATABASE_POOL_SIZE=10
# Authentication (Clerk)
# Get your keys: https://dashboard.clerk.com/
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_xxx"
CLERK_SECRET_KEY="sk_test_xxx"
# File Storage (Cloudflare R2)
# Get your keys: https://dash.cloudflare.com/
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"
# Email (Resend)
# Get your key: https://resend.com/api-keys
RESEND_API_KEY="re_xxx"
RESEND_FROM_EMAIL="noreply@yourdomain.com"
# Integrations
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"
# App Configuration
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)
```bash
# ⚠️ DO NOT COMMIT THIS FILE TO GIT
# This file contains your actual secrets for local development
# Database Configuration
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/myapp_dev"
DATABASE_POOL_SIZE=10
# Authentication (Clerk)
# TODO: Sign up at https://clerk.com and get your keys
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="<GET_FROM_CLERK_DASHBOARD>"
CLERK_SECRET_KEY="<GET_FROM_CLERK_DASHBOARD>"
# File Storage (Cloudflare R2)
# TODO: Create R2 bucket at https://dash.cloudflare.com/
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
```bash
#!/bin/bash
# Environment Variable Validation Script
# Generated by Architect AI
set -e
MISSING=()
# Function to check if variable is set
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..."
# Database
check_var "DATABASE_URL"
check_var "DATABASE_POOL_SIZE"
# Authentication
check_var "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY"
check_var "CLERK_SECRET_KEY"
# File Storage
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 ...]
# Report results
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)
```bash
# Production Environment Variables
DATABASE_URL="<PRODUCTION_DATABASE_URL>"
DATABASE_POOL_SIZE=50 # Higher for production
NEXT_PUBLIC_APP_URL="https://yourdomain.com"
NODE_ENV="production"
LOG_LEVEL="info" # Less verbose than dev
# Enable production features
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`):
```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:
```bash
# =============================================================================
# DATABASE
# =============================================================================
DATABASE_URL="..."
DATABASE_POOL_SIZE=10
View on GitHub