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).
Scanned 5/27/2026
Install via CLI
openskills install navraj007in/architecture-cowork-plugin---
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
# =============================================================================
# AUTHENTICATION
# =============================================================================
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="..."
CLERK_SECRET_KEY="..."
# =============================================================================
# FILE STORAGE
# =============================================================================
[...]
```
### 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**:
```bash
# Clerk Publishable Key (safe to expose in frontend)
# Required: Yes
# Get it: https://dashboard.clerk.com/ → API Keys
# Example: pk_test_Y2xlcmsuZXhhbXBsZS5jb20k
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_xxx"
# Clerk Secret Key (NEVER expose in frontend or git)
# Required: Yes
# Get it: https://dashboard.clerk.com/ → API Keys
# Example: sk_test_1234567890abcdef
CLERK_SECRET_KEY="sk_test_xxx"
```
### Step 4: Set Default Values
Where applicable, provide sensible defaults:
```bash
# Development defaults (can override for production)
DATABASE_POOL_SIZE=10 # 10 for dev, 50+ for prod
API_RATE_LIMIT_PER_MINUTE=60 # Generous for dev
API_TIMEOUT_MS=30000 # 30 seconds
LOG_LEVEL="debug" # debug for dev, info for prod
NODE_ENV="development"
# URLs (update for production)
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**:
```bash
# Validate DATABASE_URL is a valid PostgreSQL URL
if [[ ! "$DATABASE_URL" =~ ^postgresql:// ]]; then
echo "⚠️ DATABASE_URL must start with postgresql://"
fi
# Validate NEXT_PUBLIC_APP_URL is a valid URL
if [[ ! "$NEXT_PUBLIC_APP_URL" =~ ^https?:// ]]; then
echo "⚠️ NEXT_PUBLIC_APP_URL must start with http:// or https://"
fi
# Validate Clerk keys have correct prefix
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:
```gitignore
# 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):
1. **Environment type**: Development (default), Staging, Production
2. **Include optional variables**: Yes/No (analytics, monitoring, etc.)
3. **Framework-specific**: Next.js (default), Remix, Astro, Node.js
4. **Output format**: Bash (default), Docker Compose, Kubernetes ConfigMap
5. **Validation strictness**: Basic (default), Strict (validate formats)
**Default behavior**: Development environment, Next.js format, all variables, basic validation.
---
## Framework-Specific Variables
### Next.js
```bash
# Next.js specific (NEXT_PUBLIC_* exposed to browser)
NEXT_PUBLIC_APP_URL="http://localhost:3000"
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_xxx"
# Server-side only (never exposed)
CLERK_SECRET_KEY="sk_test_xxx"
DATABASE_URL="postgresql://..."
```
### Remix
```bash
# Remix specific (loaded via remix.config.js)
SESSION_SECRET="your-session-secret-min-32-chars"
DATABASE_URL="postgresql://..."
# Public variables (explicitly passed to client)
APP_URL="http://localhost:3000"
```
### Astro
```bash
# Astro specific (PUBLIC_* exposed to browser)
PUBLIC_APP_URL="http://localhost:3000"
PUBLIC_CLERK_KEY="pk_test_xxx"
# Server-side only
CLERK_SECRET_KEY="sk_test_xxx"
```
### Node.js / Express
```bash
# Standard Node.js
PORT=3000
NODE_ENV="development"
DATABASE_URL="postgresql://..."
JWT_SECRET="your-jwt-secret"
```
---
## Docker Compose Integration
Generate `docker-compose.yml` environment section:
```yaml
version: '3.8'
services:
app:
image: myapp:latest
env_file:
- .env.local
environment:
# Override specific variables
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):
```yaml
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):
```yaml
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**:
```bash
# Check JWT_SECRET is strong enough
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
# Check CORS_ALLOWED_ORIGINS doesn't allow all
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
1. **Clear secret markings**:
```bash
# ⚠️ SECRET - Never expose in frontend or commit to git
CLERK_SECRET_KEY="sk_test_xxx"
# ✅ PUBLIC - Safe to expose in frontend
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_xxx"
```
2. **Rotation reminders**:
```bash
# Rotate this secret every 90 days
# Last rotated: 2026-02-07
JWT_SECRET="..."
```
3. **Environment separation**:
```bash
# Use different values for dev/staging/production
# Development: sk_test_xxx
# Production: sk_live_xxx
STRIPE_SECRET_KEY="sk_test_xxx"
```
4. **Git commit prevention**:
```bash
# Add to .gitignore:
.env.local
.env.*.local
.env.production
# Pre-commit hook to catch accidental commits:
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:
- **Action**: Report all missing variables with links to obtain them
- **Example**: "Missing CLERK_SECRET_KEY - get it at https://dashboard.clerk.com/"
---
## Integration with Other Skills
### Recommended workflow:
```bash
# 1. Generate blueprint
/architect:blueprint
# 2. Scaffold project structure
/architect:scaffold
# 3. Setup environment variables
/architect:setup-env
# 4. Validate environment
chmod +x validate-env.sh
./validate-env.sh
# 5. Start development
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
```bash
/architect:setup-env
# Output:
# ✅ Created .env.example (23 variables)
# ✅ Created .env.local (ready to fill in)
# ✅ Created validate-env.sh
```
### Example 2: Production Environment
```bash
/architect:setup-env --env=production
# Output:
# ✅ Created .env.production.example
# ✅ Higher resource limits (pool size: 50)
# ✅ Production-ready defaults (LOG_LEVEL=info)
```
### Example 3: Docker Compose
```bash
/architect:setup-env --format=docker
# Output:
# ✅ Created docker-compose.yml with env_file
# ✅ Created .env for Docker Compose
```
### Example 4: Kubernetes
```bash
/architect:setup-env --format=kubernetes
# Output:
# ✅ Created k8s/configmap.yaml
# ✅ Created k8s/secret.yaml (with TODOs)
```
No comments yet. Be the first to comment!