infra-env-setup-env

Environment Management

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "infra-env-setup-env" with this command: npx skills add agents-inc/skills/agents-inc-skills-infra-env-setup-env

Environment Management

Quick Guide: Per-app .env files (apps/client-next/.env). Framework-specific prefixes (NEXTPUBLIC_, VITE__). Zod validation at startup. Maintain .env.example templates. Never commit secrets (.gitignore). Environment-based feature flags.

<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type , named constants)

(You MUST validate ALL environment variables with Zod at application startup)

(You MUST use framework-specific prefixes for client-side variables - NEXTPUBLIC* for Next.js, VITE_* for Vite)

(You MUST maintain .env.example templates with ALL required variables documented)

(You MUST never commit secrets to version control - use .env.local and CI secrets)

(You MUST use per-app .env files - NOT root-level .env files)

</critical_requirements>

Auto-detection: Environment variables, .env files, Zod validation, t3-env, @t3-oss/env, secrets management, NEXTPUBLIC prefix, VITE_ prefix, feature flags, z.stringbool

When to use:

  • Setting up Zod validation for type-safe environment variables at startup

  • Managing per-app .env files with framework-specific prefixes

  • Securing secrets (never commit, use .env.local and CI secrets)

  • Implementing environment-based feature flags

When NOT to use:

  • Runtime configuration changes (use external feature flag services like LaunchDarkly)

  • User-specific settings (use database or user preferences)

  • Frequently changing values (use configuration API or database)

  • Complex A/B testing with gradual rollouts (use dedicated feature flag services)

Key patterns covered:

  • Per-app .env files (not root-level, prevents conflicts)

  • Zod validation at startup for type safety and early failure

  • T3 Env pattern for Next.js/Vite projects (recommended)

  • Framework-specific prefixes (NEXTPUBLIC_ for client, VITE__ for Vite client)

  • .env.example templates for documentation and onboarding

Detailed Resources:

  • For code examples, see examples/ folder:

  • examples/core.md - Essential patterns (per-app .env, Zod validation)

  • examples/t3-env.md - T3 Env pattern for Next.js/Vite (recommended)

  • examples/naming-and-templates.md - Framework prefixes, .env.example

  • examples/security-and-secrets.md - Secret management

  • examples/feature-flags-and-config.md - Feature flags, centralized config

  • For decision frameworks and anti-patterns, see reference.md

Philosophy

Environment management follows the principle that configuration is code - it should be validated, typed, and versioned. The system uses per-app .env files with framework-specific prefixes, Zod validation at startup, and strict security practices to prevent secret exposure.

When to use this environment management approach:

  • Managing environment-specific configuration (API URLs, feature flags, credentials)

  • Setting up type-safe environment variables with Zod validation

  • Securing secrets with .gitignore and CI/CD secret management

  • Implementing feature flags without external services

  • Documenting required environment variables for team onboarding

When NOT to use:

  • Runtime configuration changes (use external feature flag services like LaunchDarkly)

  • User-specific settings (use database or user preferences)

  • Frequently changing values (use configuration API or database)

Core Patterns

Pattern 1: Per-App Environment Files

Each app/package has its own .env file to prevent conflicts and clarify ownership.

File Structure

apps/ ├── client-next/ │ ├── .env # Local development (NEXT_PUBLIC_API_URL) │ └── .env.production # Production overrides ├── client-react/ │ ├── .env # Local development │ └── .env.production # Production overrides └── server/ ├── .env # Local server config ├── .env.example # Template for new developers └── .env.local.example # Local overrides template

packages/ ├── api/ │ └── .env # API package config └── api-mocks/ └── .env # Mock server config

File Types and Purpose

  • .env

  • Default development values (committed for apps, gitignored for sensitive packages)

  • .env.example

  • Documentation template (committed, shows all required variables)

  • .env.local

  • Local developer overrides (gitignored, takes precedence over .env )

  • .env.production

  • Production configuration (committed or in CI secrets)

  • .env.local.example

  • Local override template (committed)

Loading Order and Precedence

Next.js loading order (highest to lowest priority):

  • .env.$(NODE_ENV).local (e.g., .env.production.local )

  • .env.local (not loaded when NODE_ENV=test )

  • .env.$(NODE_ENV) (e.g., .env.production )

  • .env

Vite loading order:

  • .env.[mode].local (e.g., .env.production.local )

  • .env.[mode] (e.g., .env.production )

  • .env.local

  • .env

Exception: Shared variables can go in turbo.json env array (see setup/monorepo/basic.md)

See examples/core.md for complete code examples.

Pattern 2: Type-Safe Environment Variables with Zod

Validate environment variables at application startup using Zod schemas.

Constants

const DEFAULT_API_TIMEOUT_MS = 30000; const DEFAULT_API_RETRY_ATTEMPTS = 3;

Validation Schema

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

const DEFAULT_API_TIMEOUT_MS = 30000;

const envSchema = z.object({ // Public variables (VITE_ prefix) VITE_API_URL: z.string().url(), VITE_API_TIMEOUT: z.coerce.number().default(DEFAULT_API_TIMEOUT_MS), // Use z.stringbool() for boolean env vars (Zod 4+) // Handles "true"/"false"/"1"/"0"/"yes"/"no" correctly VITE_ENABLE_ANALYTICS: z.stringbool().default(false), VITE_ENVIRONMENT: z.enum(["development", "staging", "production"]),

// Build-time variables MODE: z.enum(["development", "production"]), DEV: z.boolean(), PROD: z.boolean(), });

// Validate and export function validateEnv() { try { return envSchema.parse(import.meta.env); } catch (error) { if (error instanceof z.ZodError) { console.error("Invalid environment variables:"); error.errors.forEach((err) => { console.error( - ${err.path.join(".")}: ${err.message}); }); throw new Error("Invalid environment configuration"); } throw error; } }

export const env = validateEnv();

// Type-safe usage console.log(env.VITE_API_URL); // string console.log(env.VITE_API_TIMEOUT); // number console.log(env.VITE_ENABLE_ANALYTICS); // boolean

Why good: Type safety prevents runtime errors from typos or wrong types, runtime validation fails fast at startup with clear error messages, default values reduce required configuration, IDE autocomplete improves DX

Note: For Next.js/Vite projects, consider using T3 Env (@t3-oss/env-nextjs or @t3-oss/env-core ) which provides additional features like client/server variable separation and build-time validation. See examples/t3-env.md.

See examples/core.md for complete good/bad comparisons.

Pattern 3: Framework-Specific Naming Conventions

Use framework-specific prefixes for client-side variables and SCREAMING_SNAKE_CASE for all environment variables.

Mandatory Conventions

  • SCREAMING_SNAKE_CASE - All environment variables use uppercase with underscores

  • Descriptive names - Variable names clearly indicate purpose

  • Framework prefixes - Use NEXT_PUBLIC_* (Next.js) or VITE_* (Vite) for client-side variables

Framework Prefixes

Next.js:

  • NEXT_PUBLIC_*

  • Client-side accessible (embedded in bundle) - use for API URLs, public keys, feature flags

  • No prefix - Server-side only (database URLs, secret keys, API tokens)

Vite:

  • VITE_*

  • Client-side accessible (embedded in bundle) - use for API URLs, public configuration

  • No prefix - Build-time only (not exposed to client)

Node.js/Server:

  • NODE_ENV

  • Standard environment (development , production , test )

  • PORT

  • Server port number

  • No prefix - All variables available server-side

See examples/naming-and-templates.md for complete code examples with good/bad comparisons.

Integration Guide

Works with:

  • Zod: Runtime validation and type inference for environment variables

  • T3 Env: Recommended wrapper for Zod validation with client/server separation (@t3-oss/env-nextjs , @t3-oss/env-core )

  • Turborepo: Declare shared env vars in turbo.json for cache invalidation (see setup/monorepo/basic.md)

  • CI/CD: GitHub Secrets, Vercel Environment Variables for production secrets (see backend/ci-cd/basic.md)

  • Next.js: Automatic .env file loading with NEXTPUBLIC* prefix for client-side

  • Vite: Automatic .env file loading with VITE_* prefix for client-side

Replaces / Conflicts with:

  • Hardcoded configuration values (use env vars instead)

  • Runtime feature flag services for simple boolean flags (use env vars first, upgrade to LaunchDarkly if needed)

<decision_framework>

Decision Framework

Need environment configuration? ├─ Is it a secret (API key, password)? │ ├─ YES → Use .env.local (gitignored) + CI secrets │ └─ NO → Can it be public (embedded in client bundle)? │ ├─ YES → Use NEXT_PUBLIC_* or VITE_* prefix │ └─ NO → Server-side only (no prefix) ├─ Does it change per environment? │ ├─ YES → Use .env.{environment} files │ └─ NO → Use .env with defaults └─ Is it app-specific or shared? ├─ App-specific → Per-app .env file └─ Shared → Declare in turbo.json env array

See reference.md for complete decision frameworks including feature flag decisions.

</decision_framework>

<red_flags>

RED FLAGS

High Priority Issues:

  • Committing secrets to version control (.env files with real credentials)

  • Using environment variables directly without Zod validation (causes runtime errors)

  • Using NEXTPUBLIC_ or VITE__ prefix for secrets (embeds in client bundle)

Medium Priority Issues:

  • Missing .env.example documentation (poor onboarding experience)

  • Using production secrets in development (security risk)

  • Root-level .env in monorepo (causes conflicts)

Gotchas:

  • Next.js/Vite embed prefixed variables at build time, not runtime - requires rebuild to change

  • Environment variables are strings - use z.coerce.number() for numbers, use z.stringbool() for booleans (Zod 4+)

  • CRITICAL: z.coerce.boolean() converts "false" to true (string is truthy) - use z.stringbool() instead

  • Empty string env vars are NOT undefined

  • use T3 Env's emptyStringAsUndefined: true option

  • Turborepo cache is NOT invalidated by env changes unless declared in turbo.json env array

See reference.md for complete RED FLAGS, anti-patterns, and checklists.

</red_flags>

<critical_reminders>

CRITICAL REMINDERS

All code must follow project conventions in CLAUDE.md

(You MUST validate ALL environment variables with Zod at application startup)

(You MUST use framework-specific prefixes for client-side variables - NEXTPUBLIC* for Next.js, VITE_* for Vite)

(You MUST maintain .env.example templates with ALL required variables documented)

(You MUST never commit secrets to version control - use .env.local and CI secrets)

(You MUST use per-app .env files - NOT root-level .env files)

Failure to follow these rules will cause runtime errors, security vulnerabilities, and configuration confusion.

</critical_reminders>

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

infra-tooling-setup-tooling

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

meta-methodology-context-management

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

web-realtime-sse

No summary provided by upstream source.

Repository SourceNeeds Review