Skip to main content

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

  1. Go to Project SettingsEnvironment Variables
  2. Click Add Variable
  3. Enter name and value
  4. Click Save
  5. 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

FilePurposeGit
.envDefaults (non-secret)Commit
.env.localLocal overridesIgnore
.env.developmentDev-specificOptional
.env.productionProd-specificIgnore

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

  1. Immediately rotate the key in the provider's dashboard
  2. Update the new value in INSTROC
  3. Redeploy
  4. 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;
}
}
}