Environment Variables
Configure environment variables in Plainform
Plainform uses environment variables to configure integrations securely. All variables are validated at runtime using @t3-oss/env-nextjs and Zod schemas.
If any required variable is missing or invalid, the application won't start. This prevents runtime errors and security issues.
Quick Setup
Use .env.example template
The repository includes a .env.example file with all required variables. Copy it to create your .env file:
cp .env.example .envThe .env.example file contains placeholder values for all required environment variables. Edit the .env file with your actual credentials from each service provider.
Fill in your values
Open .env and replace placeholder values with your actual credentials from each service provider (see sections below for where to find them).
Validation
The app automatically validates all variables on startup via env.ts.
Complete .env Template
You'll find this template in .env.example in the repository. Copy it to .env and fill in your actual values. For production deployments, you'll need different credentials than your development environment.
# Application
SITE_URL="http://localhost:3000"
NEXT_PUBLIC_SITE_URL="http://localhost:3000"
# Clerk (Authentication)
NEXT_PUBLIC_CLERK_SIGN_IN_URL="/sign-in"
NEXT_PUBLIC_CLERK_SIGN_UP_URL="/sign-up"
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_xxxx"
CLERK_SECRET_KEY="sk_test_xxxx"
CLERK_WEBHOOK_SECRET="whsec_xxxx"
# Stripe (Payments)
STRIPE_SECRET_KEY="sk_test_xxxx"
STRIPE_PUBLISHABLE_KEY="pk_test_xxxx"
STRIPE_WEBHOOK_SECRET="whsec_xxxx"
# Supabase (Database)
DATABASE_URL="postgresql://user:password@host:6543/postgres?pgbouncer=true"
DIRECT_URL="postgresql://user:password@host:5432/postgres"
NEXT_PUBLIC_SUPABASE_URL="https://your-project-ref.supabase.co"
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY="sb_publishable_xxxx"
SUPABASE_SECRET_KEY="sb_secret_xxxx"
# AWS S3 (Storage)
AWS_ACCESS_KEY_ID="AKIAXXXXXXXXXXXX"
AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
AWS_S3_ENDPOINT="https://your-bucket-name.s3.your-region.amazonaws.com"
AWS_S3_REGION="your-region"
AWS_S3_BUCKET="your-bucket-name"
# Resend (Emails)
RESEND_API_KEY="re_xxxx"
# Mailchimp (Newsletter)
MAILCHIMP_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xx00"
MAILCHIMP_API_SERVER="xx00"
MAILCHIMP_AUDIENCE_ID="xxxxxxxxxx"
# PostHog (Analytics)
NEXT_PUBLIC_POSTHOG_KEY="phc_xxxx"
NEXT_PUBLIC_POSTHOG_HOST="https://your-region.i.posthog.com"
# Events API (Security)
EVENT_API_SECRET="your_generated_secret_here"Variable Reference
Application
| Env Variable | Type | Default |
|---|---|---|
SITE_URL | string | http://localhost:3000 |
NEXT_PUBLIC_SITE_URL | string | http://localhost:3000 |
Use http://localhost:3000 in development and your production domain in
production (e.g., https://yourdomain.com).
Clerk (Authentication)
| Env Variable | Type | Default |
|---|---|---|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | string | pk_test_xxxx |
CLERK_SECRET_KEY | string | sk_test_xxxx |
CLERK_WEBHOOK_SECRET | string | whsec_xxxx |
NEXT_PUBLIC_CLERK_SIGN_IN_URL | string | /sign-in |
NEXT_PUBLIC_CLERK_SIGN_UP_URL | string | /sign-up |
Where to find:
- Go to Clerk Dashboard
- Select your application
- Navigate to API Keys section
- Copy the keys for your environment
- For webhook secret: Webhooks → Add Endpoint → Copy Signing Secret
Stripe (Payments)
| Env Variable | Type | Default |
|---|---|---|
STRIPE_SECRET_KEY | string | sk_test_xxxx |
STRIPE_PUBLISHABLE_KEY | string | pk_test_xxxx |
STRIPE_WEBHOOK_SECRET | string | whsec_xxxx |
Where to find:
- Go to Stripe Dashboard
- Navigate to Developers → API keys
- Copy Secret key and Publishable key
- For webhook secret: Developers → Webhooks → Add endpoint → Copy signing secret
Use test keys (sk_test_, pk_test_) in development. Switch to live keys in
production.
Supabase (Database)
| Env Variable | Type | Default |
|---|---|---|
DATABASE_URL | string | postgresql://user:password@host:6543/postgres?pgbouncer=true |
DIRECT_URL | string | postgresql://user:password@host:5432/postgres |
NEXT_PUBLIC_SUPABASE_URL | string | https://your-project-ref.supabase.co |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY | string | sb_publishable_xxxx |
SUPABASE_SECRET_KEY | string | sb_secret_xxxx |
Where to find:
- Go to Supabase Dashboard
- Select your project
- Navigate to Settings → Database
- Copy the Transaction pooler connection string for
DATABASE_URL - Copy the Direct connection connection string for
DIRECT_URL - Navigate to Settings → API Keys
- Copy the Project URL for
NEXT_PUBLIC_SUPABASE_URL - Copy the Publishable key for
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY - Copy the Secret key for
SUPABASE_SECRET_KEY
DATABASE_URL uses port 6543 with pgbouncer for connection pooling.
DIRECT_URL uses port 5432 for direct connections (required for Prisma
migrations).
Never expose SUPABASE_SECRET_KEY in browser code or any variable prefixed
with NEXT_PUBLIC_. It bypasses normal Supabase access controls and belongs
only in trusted server-side code.
AWS S3 (Storage)
| Env Variable | Type | Default |
|---|---|---|
AWS_ACCESS_KEY_ID | string | - |
AWS_SECRET_ACCESS_KEY | string | - |
AWS_S3_ENDPOINT | string | https://your-bucket-name.s3.your-region.amazonaws.com |
AWS_S3_REGION | string | your-region |
AWS_S3_BUCKET | string | - |
Where to find:
- Go to AWS Console
- Navigate to IAM → Users → Create user
- Attach policy:
AmazonS3FullAccess - Create access key → Copy Access Key ID and Secret Access Key
- Navigate to S3 → Create bucket → Copy bucket name and region
Replace your-region with your actual AWS region code (e.g., us-east-1, eu-west-1, ap-southeast-1). The region must match where your S3 bucket is located.
Resend (Emails)
| Env Variable | Type | Default |
|---|---|---|
RESEND_API_KEY | string | re_xxxx |
Where to find:
- Go to Resend Dashboard
- Navigate to API Keys → Create API key → Copy the key
Mailchimp (Newsletter)
| Env Variable | Type | Default |
|---|---|---|
MAILCHIMP_API_KEY | string | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xx00 |
MAILCHIMP_API_SERVER | string | xx00 |
MAILCHIMP_AUDIENCE_ID | string | - |
Where to find:
- Go to Mailchimp Dashboard
- Navigate to Account → Extras → API keys
- Create API key → Copy the key (note the server prefix at the end, e.g.,
-us19) - Extract the server code from your API key (the part after the dash, e.g.,
us19) - Navigate to Audience → Settings → Audience name and defaults → Copy Audience ID
The MAILCHIMP_API_SERVER value comes from the end of your API key. For example, if your API key ends with -us19, use us19 as the server value.
PostHog (Analytics)
| Env Variable | Type | Default |
|---|---|---|
NEXT_PUBLIC_POSTHOG_KEY | string | phc_xxxx |
NEXT_PUBLIC_POSTHOG_HOST | string | https://your-region.i.posthog.com |
Where to find:
- Go to PostHog Dashboard
- Navigate to Project Settings
- Copy Project API Key
- Copy Host URL (choose based on your region)
PostHog Host URLs by region:
- US:
https://us.i.posthog.com - EU:
https://eu.i.posthog.com(recommended for GDPR compliance)
Replace your-region with either us or eu based on your PostHog project location.
Events API (Security)
| Env Variable | Type | Default |
|---|---|---|
EVENT_API_SECRET | string | your_generated_secret_here |
How to generate:
Use a secure random string generator like RandomKeygen to generate a strong secret key.
- Visit RandomKeygen.com
- Copy a key from "Fort Knox Passwords" or "CodeIgniter Encryption Keys" section
- Paste into your
.envfile asEVENT_API_SECRET
This secret protects your Events API endpoints. Keep it secure and never commit it to version control.
Validation Schema
All environment variables are validated in env.ts using Zod:
// From env.ts
import { createEnv } from '@t3-oss/env-nextjs';
import { z } from 'zod';
export const env = createEnv({
server: {
CLERK_SECRET_KEY: z.string().min(5),
CLERK_WEBHOOK_SECRET: z.string().min(5),
STRIPE_SECRET_KEY: z.string().min(5),
DATABASE_URL: z.string().min(5),
DIRECT_URL: z.string().min(5),
SUPABASE_SECRET_KEY: z.string().min(5),
// ... other server variables
},
client: {
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(5),
NEXT_PUBLIC_POSTHOG_KEY: z.string().min(5),
NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY: z.string().min(5),
// ... other client variables
},
experimental__runtimeEnv: {
NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL,
NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY:
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY,
// ... other client variables
},
});Local vs Production
Important: Production environments require different credentials than development. Never use test/development keys in production, and never use production keys in development.
Development (.env file):
- Use test/sandbox keys for all services
- Use
http://localhost:3000for URLs - Stripe:
sk_test_andpk_test_keys - Clerk:
pk_test_,sk_test_, and development webhook secret - Database: Development database connection
- Supabase client: Development project URL and publishable key
- Keep
.envfile in project root (already in.gitignore)
Production (Vercel/hosting platform):
- Use live/production keys for all services
- Use your production domain for URLs (e.g.,
https://yourdomain.com) - Stripe:
sk_live_andpk_live_keys - Clerk:
pk_live_,sk_live_, and production webhook secret - Database: Production database connection
- Supabase client: Production project URL and publishable key
- AWS S3: Production bucket with appropriate region
- Mailchimp: Production audience/list
- PostHog: Production project (consider EU region for GDPR)
- Set variables in hosting platform dashboard (e.g., Vercel → Settings → Environment Variables)
- Never commit production credentials to version control
Security Best Practices
- Never commit .env files - Already in
.gitignore - Use different keys per environment - Test keys in dev, live keys in production
- Rotate keys regularly - Especially after team member changes
- Limit key permissions - Use least-privilege principle (e.g., read-only keys where possible)
- Validate on startup - The
env.tsschema prevents missing/invalid variables
Never expose server-side variables to the client. Only variables prefixed with
NEXT_PUBLIC_ are accessible in the browser.
Troubleshooting
Next Steps
- Git Workflow - Set up version control
- Build & Deploy - Deploy to production
- Authentication Setup - Configure Clerk
- Payments Setup - Configure Stripe
How is this guide ?
Last updated on