setup-env

star 2

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).

navraj007in By navraj007in schedule Updated 4/1/2026

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 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)

# ⚠️ 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

#!/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)

# 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):

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
# =============================================================================

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:

# 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:

# 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:

# 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:

# 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

# 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

# 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

# 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

# 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:

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):

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:

# 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:
# ⚠️ 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"
  1. Rotation reminders:
# Rotate this secret every 90 days
# Last rotated: 2026-02-07
JWT_SECRET="..."
  1. Environment separation:
# Use different values for dev/staging/production
# Development: sk_test_xxx
# Production: sk_live_xxx
STRIPE_SECRET_KEY="sk_test_xxx"
  1. Git commit prevention:
# 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:

# 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

/architect:setup-env

# Output:
# ✅ Created .env.example (23 variables)
# ✅ Created .env.local (ready to fill in)
# ✅ Created validate-env.sh

Example 2: Production Environment

/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

/architect:setup-env --format=docker

# Output:
# ✅ Created docker-compose.yml with env_file
# ✅ Created .env for Docker Compose

Example 4: Kubernetes

/architect:setup-env --format=kubernetes

# Output:
# ✅ Created k8s/configmap.yaml
# ✅ Created k8s/secret.yaml (with TODOs)
Install via CLI
npx skills add https://github.com/navraj007in/architecture-cowork-plugin --skill setup-env
Repository Details
star Stars 2
call_split Forks 1
navigation Branch main
article Path SKILL.md
More from Creator