Back to skills
SkillHub ClubRun DevOpsFull StackDevOpsTesting

env-config

This skill manages environment variables and secrets across development, staging, and production environments, helping users add new variables, configure stage-specific settings, set up secrets for deployment, and debug environment-related issues.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
20
Hot score
87
Updated
March 20, 2026
Overall rating
C4.4
Composite score
4.4
Best-practice grade
B82.7

Install command

npx @skill-hub/cli install sgcarstrends-backend-env-config

Repository

sgcarstrends/backend

Skill path: .claude/skills/env-config

This skill manages environment variables and secrets across development, staging, and production environments, helping users add new variables, configure stage-specific settings, set up secrets for deployment, and debug environment-related issues.

Open repository

Best for

Primary workflow: Run DevOps.

Technical facets: Full Stack, DevOps, Testing.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: sgcarstrends.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install env-config into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/sgcarstrends/backend before adding env-config to shared team environments
  • Use env-config for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: env-config
description: Manage environment variables across dev/staging/prod environments and SST config. Use when adding new secrets, updating environment-specific configs, or debugging environment issues.
allowed-tools: Read, Edit, Grep
---

# Environment Configuration Skill

This skill helps you manage environment variables and secrets across the monorepo.

## When to Use This Skill

- Adding new environment variables
- Configuring stage-specific variables
- Setting up secrets for deployment
- Debugging environment variable issues
- Managing API keys and tokens
- Configuring database connections
- Setting up third-party integrations

## Environment File Structure

```
sgcarstrends/
├── .env.example              # Example env file (committed)
├── .env.local                # Local development (not committed)
├── .env.test                 # Test environment (not committed)
├── apps/
│   ├── api/
│   │   ├── .env.example
│   │   └── .env.local
│   └── web/
│       ├── .env.example
│       └── .env.local
└── packages/
    └── database/
        ├── .env.example
        └── .env.local
```

## Environment Variable Categories

### Database

```env
# PostgreSQL
DATABASE_URL=postgresql://user:password@host:5432/database

# Connection pooling (optional)
DATABASE_POOL_MIN=2
DATABASE_POOL_MAX=10
```

### Redis

```env
# Upstash Redis
UPSTASH_REDIS_REST_URL=https://your-instance.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token-here
```

### AI Services

```env
# Google Gemini
GOOGLE_GEMINI_API_KEY=your-api-key
GEMINI_MODEL=gemini-1.5-pro-latest
```

### Workflows & Queues

```env
# QStash
QSTASH_TOKEN=your-qstash-token
QSTASH_CURRENT_SIGNING_KEY=your-signing-key
QSTASH_NEXT_SIGNING_KEY=your-next-signing-key
```

### Social Media

```env
# Telegram
TELEGRAM_BOT_TOKEN=your-bot-token
TELEGRAM_CHAT_ID=your-chat-id

# Twitter
TWITTER_BEARER_TOKEN=your-bearer-token

# LinkedIn
LINKEDIN_ACCESS_TOKEN=your-access-token
LINKEDIN_ORG_ID=your-org-id
```

### Error Reporting

```env
# Discord (workflow error logging only)
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
```

### Storage

```env
# Vercel Blob
BLOB_READ_WRITE_TOKEN=vercel_blob_token
```

### Next.js Public Variables

```env
# Public variables (exposed to browser)
NEXT_PUBLIC_API_URL=https://api.sgcarstrends.com
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX
NEXT_PUBLIC_SITE_URL=https://sgcarstrends.com
```

### Build & Runtime

```env
# Node environment
NODE_ENV=development  # development | production | test

# Next.js
NEXT_TELEMETRY_DISABLED=1
```

## Environment File Examples

### Root .env.example

```env
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/sgcarstrends

# Redis
UPSTASH_REDIS_REST_URL=https://your-instance.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token-here

# AI Services
GOOGLE_GEMINI_API_KEY=your-api-key

# Workflows
QSTASH_TOKEN=your-qstash-token

# Social Media
TELEGRAM_BOT_TOKEN=your-bot-token
TELEGRAM_CHAT_ID=your-chat-id

# Error Reporting (Discord webhook)
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...

# Storage
BLOB_READ_WRITE_TOKEN=vercel_blob_token
```

### apps/web/.env.example

```env
# API
NEXT_PUBLIC_API_URL=http://localhost:3000

# Database (for server components)
DATABASE_URL=postgresql://user:password@localhost:5432/sgcarstrends

# Redis
UPSTASH_REDIS_REST_URL=https://your-instance.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token-here

# Analytics
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

# Site URL
NEXT_PUBLIC_SITE_URL=http://localhost:3001
```

### apps/api/.env.example

```env
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/sgcarstrends

# Redis
UPSTASH_REDIS_REST_URL=https://your-instance.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token-here

# All other API-specific vars...
```

## Stage-Specific Configuration

### Development

```env
# .env.local (development)
NODE_ENV=development
DATABASE_URL=postgresql://localhost:5432/sgcarstrends_dev
NEXT_PUBLIC_API_URL=http://localhost:3000
NEXT_PUBLIC_SITE_URL=http://localhost:3001
```

### Staging

```env
# Staging (set in SST or CI/CD)
NODE_ENV=production
DATABASE_URL=postgresql://...staging...
NEXT_PUBLIC_API_URL=https://api.staging.sgcarstrends.com
NEXT_PUBLIC_SITE_URL=https://staging.sgcarstrends.com
```

### Production

```env
# Production (set in SST or CI/CD)
NODE_ENV=production
DATABASE_URL=postgresql://...production...
NEXT_PUBLIC_API_URL=https://api.sgcarstrends.com
NEXT_PUBLIC_SITE_URL=https://sgcarstrends.com
```

## SST Configuration

### Using Environment Variables in SST

```typescript
// infra/api.ts
import { StackContext, Function } from "sst/constructs";

export function API({ stack, app }: StackContext) {
  const api = new Function(stack, "api", {
    handler: "apps/api/src/index.handler",
    environment: {
      NODE_ENV: app.stage === "production" ? "production" : "development",
      DATABASE_URL: process.env.DATABASE_URL!,
      UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL!,
      UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN!,
      GOOGLE_GEMINI_API_KEY: process.env.GOOGLE_GEMINI_API_KEY!,
      QSTASH_TOKEN: process.env.QSTASH_TOKEN!,
      DISCORD_WEBHOOK_URL: process.env.DISCORD_WEBHOOK_URL!,
      TELEGRAM_BOT_TOKEN: process.env.TELEGRAM_BOT_TOKEN!,
      TELEGRAM_CHAT_ID: process.env.TELEGRAM_CHAT_ID!,
    },
  });

  return { api };
}
```

### SST Secrets

For sensitive values, use SST secrets:

```bash
# Set secret for specific stage
npx sst secrets set DATABASE_URL "postgresql://..." --stage production
npx sst secrets set UPSTASH_REDIS_REST_TOKEN "token" --stage production

# List secrets
npx sst secrets list --stage production

# Remove secret
npx sst secrets remove DATABASE_URL --stage production
```

Access in code:

```typescript
import { Config } from "sst/node/config";

export const handler = async () => {
  const databaseUrl = Config.DATABASE_URL;
  // Use secret...
};
```

Define in SST config:

```typescript
// infra/api.ts
import { Config } from "sst/constructs";

export function API({ stack }: StackContext) {
  const DATABASE_URL = new Config.Secret(stack, "DATABASE_URL");

  const api = new Function(stack, "api", {
    handler: "apps/api/src/index.handler",
    bind: [DATABASE_URL],
  });
}
```

## TypeScript Type Safety

### Declare Environment Variables

```typescript
// env.d.ts (root or app-specific)
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      // Database
      DATABASE_URL: string;

      // Redis
      UPSTASH_REDIS_REST_URL: string;
      UPSTASH_REDIS_REST_TOKEN: string;

      // AI
      GOOGLE_GEMINI_API_KEY: string;
      GEMINI_MODEL?: string;

      // Workflows
      QSTASH_TOKEN: string;

      // Social Media
      TELEGRAM_BOT_TOKEN: string;
      TELEGRAM_CHAT_ID: string;

      // Error Reporting
      DISCORD_WEBHOOK_URL: string;

      // Storage
      BLOB_READ_WRITE_TOKEN: string;

      // Next.js Public
      NEXT_PUBLIC_API_URL: string;
      NEXT_PUBLIC_SITE_URL: string;
      NEXT_PUBLIC_GA_MEASUREMENT_ID?: string;

      // Build
      NODE_ENV: "development" | "production" | "test";
    }
  }
}

export {};
```

### Validate Environment Variables

```typescript
// lib/env.ts
import { z } from "zod";

const envSchema = z.object({
  // Database
  DATABASE_URL: z.string().url(),

  // Redis
  UPSTASH_REDIS_REST_URL: z.string().url(),
  UPSTASH_REDIS_REST_TOKEN: z.string().min(1),

  // AI
  GOOGLE_GEMINI_API_KEY: z.string().min(1),

  // Workflows
  QSTASH_TOKEN: z.string().min(1),

  // Social Media
  TELEGRAM_BOT_TOKEN: z.string().optional(),
  TELEGRAM_CHAT_ID: z.string().optional(),

  // Error Reporting
  DISCORD_WEBHOOK_URL: z.string().url().optional(),

  // Next.js
  NEXT_PUBLIC_API_URL: z.string().url(),
  NEXT_PUBLIC_SITE_URL: z.string().url(),

  // Build
  NODE_ENV: z.enum(["development", "production", "test"]),
});

export const env = envSchema.parse(process.env);

// Usage
import { env } from "./lib/env";
const db = new Database(env.DATABASE_URL);
```

## Loading Environment Variables

### Next.js

Next.js automatically loads `.env.local`:

```typescript
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  env: {
    CUSTOM_KEY: process.env.CUSTOM_KEY,
  },
};

export default nextConfig;
```

### Drizzle Studio

```typescript
// packages/database/drizzle.config.ts
import { defineConfig } from "drizzle-kit";
import * as dotenv from "dotenv";

// Load environment variables
dotenv.config({ path: "../../.env.local" });

export default defineConfig({
  schema: "./src/db/schema",
  out: "./migrations",
  dialect: "postgresql",
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
});
```

### Vitest

```typescript
// vitest.config.ts
import { defineConfig } from "vitest/config";
import * as dotenv from "dotenv";

// Load test environment
dotenv.config({ path: ".env.test" });

export default defineConfig({
  test: {
    env: {
      DATABASE_URL: process.env.DATABASE_URL!,
    },
  },
});
```

## GitHub Actions

### Secrets Configuration

Set secrets in GitHub:
1. Go to repository Settings
2. Secrets and variables → Actions
3. Add repository secrets

### Use in Workflows

```yaml
# .github/workflows/deploy-prod.yml
name: Deploy Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      DATABASE_URL: ${{ secrets.DATABASE_URL }}
      UPSTASH_REDIS_REST_URL: ${{ secrets.UPSTASH_REDIS_REST_URL }}
      UPSTASH_REDIS_REST_TOKEN: ${{ secrets.UPSTASH_REDIS_REST_TOKEN }}
      GOOGLE_GEMINI_API_KEY: ${{ secrets.GOOGLE_GEMINI_API_KEY }}

    steps:
      - uses: actions/checkout@v4

      - name: Deploy
        run: pnpm deploy:prod
```

## Debugging Environment Issues

### Check Variables Are Set

```typescript
// Check at runtime
console.log("DATABASE_URL:", process.env.DATABASE_URL ? "✓ Set" : "✗ Not set");
console.log("REDIS_URL:", process.env.UPSTASH_REDIS_REST_URL ? "✓ Set" : "✗ Not set");

// Fail early if missing
if (!process.env.DATABASE_URL) {
  throw new Error("DATABASE_URL is required");
}
```

### Use dotenv-cli

```bash
# Install
pnpm add -D dotenv-cli

# Run command with env file
pnpm dotenv -e .env.local -- pnpm dev

# Run tests with test env
pnpm dotenv -e .env.test -- pnpm test
```

### Debug Script

```typescript
// scripts/check-env.ts
import * as dotenv from "dotenv";

dotenv.config({ path: ".env.local" });

const requiredVars = [
  "DATABASE_URL",
  "UPSTASH_REDIS_REST_URL",
  "UPSTASH_REDIS_REST_TOKEN",
];

console.log("Checking environment variables...\n");

let missing = 0;

for (const varName of requiredVars) {
  const value = process.env[varName];

  if (value) {
    console.log(`✓ ${varName}`);
  } else {
    console.log(`✗ ${varName} - MISSING`);
    missing++;
  }
}

if (missing > 0) {
  console.error(`\n❌ ${missing} required variables are missing!`);
  process.exit(1);
} else {
  console.log("\n✅ All required variables are set!");
}
```

Add to package.json:

```json
{
  "scripts": {
    "check-env": "tsx scripts/check-env.ts"
  }
}
```

## Common Patterns

### Conditional Loading

```typescript
// Load different configs based on environment
const config = {
  development: {
    apiUrl: "http://localhost:3000",
    debug: true,
  },
  production: {
    apiUrl: process.env.NEXT_PUBLIC_API_URL,
    debug: false,
  },
  test: {
    apiUrl: "http://localhost:3000",
    debug: false,
  },
}[process.env.NODE_ENV || "development"];
```

### Default Values

```typescript
const config = {
  apiUrl: process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000",
  cacheTimeout: parseInt(process.env.CACHE_TIMEOUT || "3600", 10),
  enableFeature: process.env.ENABLE_FEATURE === "true",
};
```

### Required vs Optional

```typescript
// Required (throw if missing)
const databaseUrl = process.env.DATABASE_URL!;

// Optional (with default)
const cacheTimeout = process.env.CACHE_TIMEOUT || "3600";

// Optional (may be undefined)
const optionalKey: string | undefined = process.env.OPTIONAL_KEY;
```

## Security Best Practices

### 1. Never Commit Secrets

```gitignore
# .gitignore
.env
.env.local
.env.*.local
.env.development.local
.env.test.local
.env.production.local
```

### 2. Use Environment-Specific Files

```
.env.example        ✅ Committed (no sensitive data)
.env.local          ❌ Not committed (has secrets)
.env.development    ❌ Not committed
.env.production     ❌ Not committed
```

### 3. Rotate Secrets Regularly

```bash
# Update secret for all stages
npx sst secrets set API_KEY "new-key" --stage dev
npx sst secrets set API_KEY "new-key" --stage staging
npx sst secrets set API_KEY "new-key" --stage production
```

### 4. Least Privilege

Only grant necessary permissions:
- Database: Use read-only user for read operations
- API keys: Use restricted scopes
- AWS: Use IAM roles with minimal permissions

### 5. Audit Access

```bash
# Check who has access to secrets
npx sst secrets list --stage production

# Review CloudWatch logs for unauthorized access
```

## Testing with Environment Variables

```typescript
// __tests__/api.test.ts
import { describe, it, expect, beforeAll } from "vitest";

describe("API Tests", () => {
  beforeAll(() => {
    // Set test environment variables
    process.env.DATABASE_URL = "postgresql://test:test@localhost:5432/test";
    process.env.UPSTASH_REDIS_REST_URL = "https://test.upstash.io";
  });

  it("uses correct environment", () => {
    expect(process.env.DATABASE_URL).toContain("test");
  });
});
```

## Troubleshooting

### Variables Not Loading

**Issue**: Environment variables are undefined
**Solutions**:
1. Check file name is exactly `.env.local`
2. Restart development server
3. Verify file is in correct directory
4. Check for syntax errors in .env file

### Next.js Public Variables

**Issue**: `NEXT_PUBLIC_` variables not available in browser
**Solutions**:
1. Ensure variable starts with `NEXT_PUBLIC_`
2. Restart Next.js dev server
3. Check browser console for actual value

### SST Secrets Not Working

**Issue**: SST secrets return undefined
**Solutions**:
1. Verify secret is set for correct stage
2. Check `bind` configuration in SST
3. Use `Config` import from `sst/node/config`

## References

- Next.js Environment Variables: https://nextjs.org/docs/app/building-your-application/configuring/environment-variables
- SST Config: https://docs.sst.dev/config
- Related files:
  - `.env.example` - Example environment file
  - `infra/` - SST infrastructure with env config
  - Root CLAUDE.md - Project documentation

## Best Practices

1. **Use .env.example**: Provide template for required variables
2. **Type Safety**: Define types for environment variables
3. **Validation**: Validate on application startup
4. **SST Secrets**: Use for sensitive production values
5. **Never Commit**: Add `.env.local` to `.gitignore`
6. **Document**: Comment what each variable is for
7. **Stage-Specific**: Use different values per environment
8. **Fail Fast**: Validate required variables at startup