Environment Variables
Store secrets and configuration that vary between environments.
What Are Environment Variables?
Environment variables are key-value pairs that:
- Store sensitive data (API keys, secrets)
- Configure behavior per environment
- Keep secrets out of your code
Example:
STRIPE_SECRET_KEY=sk_live_xxxxx
DATABASE_URL=postgres://user:pass@host/db
Adding Variables
In INSTROC
- Go to Project Settings → Environment Variables
- Click Add Variable
- Enter name and value
- Click Save
- Redeploy for changes to take effect
Variable Types
Server-side only (default):
- Available in API routes and server components
- Never exposed to browser
- Use for secrets
Public (prefix with NEXT_PUBLIC_):
- Available everywhere including browser
- Safe for non-sensitive config
- Use for public API keys
Using Variables
In Server Code
// API routes, server components, server actions
const stripeKey = process.env.STRIPE_SECRET_KEY;
const response = await fetch('https://api.stripe.com/...', {
headers: { Authorization: `Bearer ${stripeKey}` }
});
In Client Code
Only NEXT_PUBLIC_ variables are available:
// Client components
const publishableKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
Type-Safe Access
// lib/env.ts
export const env = {
stripeKey: process.env.STRIPE_SECRET_KEY!,
databaseUrl: process.env.DATABASE_URL!,
publicUrl: process.env.NEXT_PUBLIC_APP_URL!,
};
// Usage
import { env } from '@/lib/env';
console.log(env.stripeKey);
Common Variables
Required by INSTROC
These are set automatically:
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...
Commonly Added
# Payments
STRIPE_SECRET_KEY=sk_live_xxx
STRIPE_PUBLISHABLE_KEY=pk_live_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
# Email
RESEND_API_KEY=re_xxx
# Analytics
NEXT_PUBLIC_GA_ID=G-XXXXX
# App Config
NEXT_PUBLIC_APP_URL=https://www.example.com
Environments
Development vs Production
Use different values per environment:
Development:
STRIPE_SECRET_KEY=sk_test_xxx
DATABASE_URL=postgres://localhost/myapp_dev
Production:
STRIPE_SECRET_KEY=sk_live_xxx
DATABASE_URL=postgres://prod-server/myapp
Setting Per Environment
In INSTROC, variables apply to all deployments. Use naming to differentiate:
STRIPE_SECRET_KEY_TEST=sk_test_xxx
STRIPE_SECRET_KEY_LIVE=sk_live_xxx
Then in code:
const key = process.env.NODE_ENV === 'production'
? process.env.STRIPE_SECRET_KEY_LIVE
: process.env.STRIPE_SECRET_KEY_TEST;
Local Development
Create .env.local
For local development, create .env.local in your project root:
# .env.local (never commit this file!)
STRIPE_SECRET_KEY=sk_test_xxx
DATABASE_URL=postgres://localhost/myapp
.env Files
| File | Purpose | Git |
|---|---|---|
.env | Defaults (non-secret) | Commit |
.env.local | Local overrides | Ignore |
.env.development | Dev-specific | Optional |
.env.production | Prod-specific | Ignore |
.gitignore
Always ignore files with secrets:
.env.local
.env*.local
.env.production
Security Best Practices
Never Commit Secrets
❌ Don't do this:
const apiKey = 'sk_live_123456789'; // Hardcoded secret!
✅ Do this:
const apiKey = process.env.STRIPE_SECRET_KEY;
Don't Expose Server Variables
❌ Don't do this:
// In client component
const secret = process.env.SECRET_KEY; // Won't work, and shouldn't!
✅ Do this:
// Use NEXT_PUBLIC_ prefix for client-safe values
const publicId = process.env.NEXT_PUBLIC_APP_ID;
Rotate Compromised Keys
If a secret is leaked:
- Immediately rotate the key in the provider's dashboard
- Update the new value in INSTROC
- Redeploy
- Review access logs
Limit Permissions
When creating API keys:
- Use the minimum required permissions
- Create separate keys for different services
- Use read-only keys when possible
Validation
Check Required Variables
// lib/env.ts
const requiredEnvVars = [
'DATABASE_URL',
'STRIPE_SECRET_KEY',
] as const;
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`Missing required environment variable: ${envVar}`);
}
}
Zod Validation
import { z } from 'zod';
const envSchema = z.object({
DATABASE_URL: z.string().url(),
STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
NODE_ENV: z.enum(['development', 'production', 'test']),
});
export const env = envSchema.parse(process.env);
Troubleshooting
Variable Not Available
Server-side variable in client code:
- Add
NEXT_PUBLIC_prefix if safe to expose - Or fetch from API route
Not set in environment:
- Check spelling (case-sensitive)
- Verify in Project Settings
- Redeploy after changes
Cached value:
- Redeploy to pick up changes
- Clear build cache
Undefined in Production
- Variables set locally don't auto-sync
- Must be set in INSTROC dashboard
- Redeploy after adding
Type Errors
TypeScript doesn't know about env vars:
// Tell TypeScript about your env vars
declare global {
namespace NodeJS {
interface ProcessEnv {
STRIPE_SECRET_KEY: string;
DATABASE_URL: string;
}
}
}