Environment Variables Complete reference of all environment variables with Zod validation schemas
Coldstart generates a packages/env/ package with Zod-validated environment variables using @t3-oss/env. Every variable is validated at startup — if a required variable is missing or malformed, the app fails immediately with a clear error message.
packages/env/
├── src/
│ ├── server.ts # API env vars (@t3-oss/env-nextjs or @t3-oss/env-core)
│ ├── web.ts # Web env vars (@t3-oss/env-nextjs)
│ └── native.ts # Mobile env vars (plain Zod schema)
├── package.json
└── tsconfig.json
Each platform imports its own env module:
// In apps/api
import { env } from " @scope/env/server " ;
// In apps/web
import { env } from " @scope/env/web " ;
// In apps/mobile
import { env } from " @scope/env/native " ;
API (Server) Web Mobile
Variable Zod Schema Required Description NODE_ENVz.enum(["development", "production", "test"]).default("development")No Runtime environment DATABASE_URLz.string().url()Yes PostgreSQL connection string (Neon) BETTER_AUTH_SECRETz.string().min(32)Yes Auth signing secret (32+ chars) BETTER_AUTH_URLz.string().url()Yes Auth base URL (API URL)
Generated for each OAuth provider selected:
Variable Zod Schema Required Condition GOOGLE_CLIENT_IDz.string().min(1)Yes Google OAuth enabled GOOGLE_CLIENT_SECRETz.string().min(1)Yes Google OAuth enabled GITHUB_CLIENT_IDz.string().min(1)Yes GitHub OAuth enabled GITHUB_CLIENT_SECRETz.string().min(1)Yes GitHub OAuth enabled APPLE_CLIENT_IDz.string().min(1)Yes Apple OAuth enabled APPLE_CLIENT_SECRETz.string().min(1)Yes Apple OAuth enabled
Variable Zod Schema Required Condition STRIPE_SECRET_KEYz.string().startsWith("sk_")Yes Stripe billing STRIPE_WEBHOOK_SECRETz.string().startsWith("whsec_")Yes Stripe billing
Variable Zod Schema Required Condition POLAR_ACCESS_TOKENz.string().min(1)Yes Polar billing POLAR_WEBHOOK_SECRETz.string().min(1)Yes Polar billing
Variable Zod Schema Required Condition REVENUECAT_WEBHOOK_AUTH_KEYz.string().min(1)Yes Billing + mobile platform
packages/env/src/server.ts (Stripe + Google OAuth example) import { createEnv } from " @t3-oss/env-nextjs " ;
import { z } from " zod " ;
export const env = createEnv ({
server : {
NODE_ENV : z . enum ([ " development " , " production " , " test " ]). default ( " development " ),
DATABASE_URL : z . string (). url (),
BETTER_AUTH_SECRET : z . string (). min ( 32 ),
BETTER_AUTH_URL : z . string (). url (),
GOOGLE_CLIENT_ID : z . string (). min ( 1 ),
GOOGLE_CLIENT_SECRET : z . string (). min ( 1 ),
STRIPE_SECRET_KEY : z . string (). startsWith ( " sk_ " ),
STRIPE_WEBHOOK_SECRET : z . string (). startsWith ( " whsec_ " ),
},
client : {},
runtimeEnv : {
NODE_ENV : process . env . NODE_ENV ,
DATABASE_URL : process . env . DATABASE_URL ,
BETTER_AUTH_SECRET : process . env . BETTER_AUTH_SECRET ,
BETTER_AUTH_URL : process . env . BETTER_AUTH_URL ,
GOOGLE_CLIENT_ID : process . env . GOOGLE_CLIENT_ID ,
GOOGLE_CLIENT_SECRET : process . env . GOOGLE_CLIENT_SECRET ,
STRIPE_SECRET_KEY : process . env . STRIPE_SECRET_KEY ,
STRIPE_WEBHOOK_SECRET : process . env . STRIPE_WEBHOOK_SECRET ,
},
}); Variable Zod Schema Required Description NODE_ENVz.enum(["development", "production", "test"]).default("development")No Runtime environment NEXT_PUBLIC_API_URLz.string().url().optional()No API base URL
Variable Zod Schema Required Condition NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYz.string().startsWith("pk_").optional()No Stripe billing
Variable Zod Schema Required Condition NEXT_PUBLIC_POSTHOG_KEY— No Analytics enabled NEXT_PUBLIC_POSTHOG_HOST— No Analytics enabled
PostHog variables are used directly via process.env in the PostHog init code, not through the @t3-oss/env schema. PostHog gracefully handles missing keys by not initializing.
packages/env/src/web.ts (Stripe example) import { createEnv } from " @t3-oss/env-nextjs " ;
import { z } from " zod " ;
export const env = createEnv ({
server : {
NODE_ENV : z . enum ([ " development " , " production " , " test " ]). default ( " development " ),
},
client : {
NEXT_PUBLIC_API_URL : z . string (). url (). optional (),
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY : z . string (). startsWith ( " pk_ " ). optional (),
},
runtimeEnv : {
NODE_ENV : process . env . NODE_ENV ,
NEXT_PUBLIC_API_URL : process . env . NEXT_PUBLIC_API_URL ,
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY : process . env . NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY ,
},
}); Variable Zod Schema Required Description EXPO_PUBLIC_API_URLz.string().url().optional()No API base URL
Variable Zod Schema Required Condition EXPO_PUBLIC_REVENUECAT_IOS_KEYz.string().optional()No Billing enabled EXPO_PUBLIC_REVENUECAT_ANDROID_KEYz.string().optional()No Billing enabled
Variable Zod Schema Required Condition EXPO_PUBLIC_POSTHOG_KEY— No Analytics enabled EXPO_PUBLIC_POSTHOG_HOST— No Analytics enabled
Mobile uses a plain Zod schema (not @t3-oss/env) because Expo doesn't support the Next.js env pattern:
packages/env/src/native.ts (with billing) import { z } from " zod " ;
const schema = z . object ({
EXPO_PUBLIC_API_URL : z . string (). url (). optional (),
EXPO_PUBLIC_REVENUECAT_IOS_KEY : z . string (). optional (),
EXPO_PUBLIC_REVENUECAT_ANDROID_KEY : z . string (). optional (),
});
export const env = schema . parse ({
EXPO_PUBLIC_API_URL : process . env . EXPO_PUBLIC_API_URL ,
EXPO_PUBLIC_REVENUECAT_IOS_KEY : process . env . EXPO_PUBLIC_REVENUECAT_IOS_KEY ,
EXPO_PUBLIC_REVENUECAT_ANDROID_KEY : process . env . EXPO_PUBLIC_REVENUECAT_ANDROID_KEY ,
});
Each app has its own .env file. Coldstart generates .env.example files with placeholder values:
File Used by Git-tracked apps/api/.envAPI server No (in .gitignore) apps/api/.env.exampleReference Yes apps/web/.envNext.js web app No (in .gitignore) apps/web/.env.exampleReference Yes apps/mobile/.envExpo mobile app No (in .gitignore) apps/mobile/.env.exampleReference Yes
The env validation runs at import time . When your app starts and imports @scope/env/server, the Zod schema validates every variable immediately:
❌ Invalid environment variables:
DATABASE_URL: Required
BETTER_AUTH_SECRET: String must contain at least 32 character(s)
STRIPE_SECRET_KEY: Invalid input: must start with "sk_"
This fail-fast approach catches configuration errors before any request is served.
If your API fails to start with env validation errors, check that all required variables are set in apps/api/.env. Copy from .env.example as a starting point.
Not all variables are always generated. The env schema adapts to your project configuration:
Variable Platform Condition Required NODE_ENVAPI, Web Always No (defaults to development) DATABASE_URLAPI Always Yes BETTER_AUTH_SECRETAPI Always Yes BETTER_AUTH_URLAPI Always Yes GOOGLE_CLIENT_IDAPI Google OAuth Yes GOOGLE_CLIENT_SECRETAPI Google OAuth Yes GITHUB_CLIENT_IDAPI GitHub OAuth Yes GITHUB_CLIENT_SECRETAPI GitHub OAuth Yes APPLE_CLIENT_IDAPI Apple OAuth Yes APPLE_CLIENT_SECRETAPI Apple OAuth Yes STRIPE_SECRET_KEYAPI Stripe billing Yes STRIPE_WEBHOOK_SECRETAPI Stripe billing Yes POLAR_ACCESS_TOKENAPI Polar billing Yes POLAR_WEBHOOK_SECRETAPI Polar billing Yes REVENUECAT_WEBHOOK_AUTH_KEYAPI Billing + mobile Yes NEXT_PUBLIC_API_URLWeb Always No NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYWeb Stripe billing No NEXT_PUBLIC_POSTHOG_KEYWeb Analytics No NEXT_PUBLIC_POSTHOG_HOSTWeb Analytics No EXPO_PUBLIC_API_URLMobile Always No EXPO_PUBLIC_REVENUECAT_IOS_KEYMobile Billing No EXPO_PUBLIC_REVENUECAT_ANDROID_KEYMobile Billing No EXPO_PUBLIC_POSTHOG_KEYMobile Analytics No EXPO_PUBLIC_POSTHOG_HOSTMobile Analytics No