Production Environment Variables for Next.js Supabase SaaS
Complete reference for generating and configuring production environment variables in your MakerKit Next.js Supabase application.
Generate and configure environment variables for your MakerKit Next.js Supabase Turbo production deployment. MakerKit uses Zod schemas to validate all variables at build time, catching configuration errors before deployment.
Generate Environment Variables
MakerKit provides an interactive generator that walks you through each required variable:
pnpm turbo gen envThis command:
- Prompts you for each variable value
- Uses defaults from your existing
.envfiles when available - Creates a file at
turbo/generators/templates/env/.env.local
Copy the contents of this file to your hosting provider's environment variable settings.
Never commit this file
The generated .env.local contains secrets. It's git-ignored by default, but verify it's not being tracked.
Validate Environment Variables
After generating or manually setting variables, validate them:
turbo gen validate-envThis checks that all required variables are present and correctly formatted.
Required Variables Reference
Application Settings
| Variable | Description | Example |
|---|---|---|
NEXT_PUBLIC_SITE_URL | Your production URL (no trailing slash) | https://yourdomain.com |
NEXT_PUBLIC_PRODUCT_NAME | Product name shown in UI | MyApp |
NEXT_PUBLIC_SITE_TITLE | Browser tab title | MyApp - Build faster |
NEXT_PUBLIC_SITE_DESCRIPTION | Meta description for SEO | The fastest way to build SaaS |
NEXT_PUBLIC_DEFAULT_THEME_MODE | Default theme | light, dark, or system |
NEXT_PUBLIC_DEFAULT_LOCALE | Default language | en |
Supabase Configuration
| Variable | Description | Where to Find |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL | Supabase project URL | Dashboard > Settings > API |
NEXT_PUBLIC_SUPABASE_PUBLIC_KEY | Supabase anon key | Dashboard > Settings > API |
SUPABASE_SECRET_KEY | Supabase service role key | Dashboard > Settings > API |
SUPABASE_DB_WEBHOOK_SECRET | Secret for webhook authentication | Generate with openssl rand -base64 32 |
Keep secrets private
SUPABASE_SECRET_KEY bypasses Row Level Security. Never expose it in client-side code.
Authentication Settings
| Variable | Description | Default |
|---|---|---|
NEXT_PUBLIC_AUTH_PASSWORD | Enable email/password login | true |
NEXT_PUBLIC_AUTH_MAGIC_LINK | Enable magic link login | false |
Feature Flags
| Variable | Description | Default |
|---|---|---|
NEXT_PUBLIC_ENABLE_THEME_TOGGLE | Show theme switcher in UI | true |
NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_DELETION | Allow users to delete their account | false |
NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING | Enable billing for personal accounts | false |
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS | Enable team/organization accounts | true |
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_DELETION | Allow team deletion | false |
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_BILLING | Enable billing for teams | false |
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_CREATION | Allow creating new teams | true |
NEXT_PUBLIC_ENABLE_NOTIFICATIONS | Show notification bell in UI | true |
NEXT_PUBLIC_REALTIME_NOTIFICATIONS | Use Supabase realtime for notifications | false |
NEXT_PUBLIC_ENABLE_VERSION_UPDATER | Show version update popup | false |
Billing Configuration
Stripe
| Variable | Description | Where to Find |
|---|---|---|
NEXT_PUBLIC_BILLING_PROVIDER | Set to stripe | - |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Stripe publishable key | Stripe Dashboard > API Keys |
STRIPE_SECRET_KEY | Stripe secret key | Stripe Dashboard > API Keys |
STRIPE_WEBHOOK_SECRET | Webhook signing secret | Stripe Dashboard > Webhooks |
Lemon Squeezy
| Variable | Description | Where to Find |
|---|---|---|
NEXT_PUBLIC_BILLING_PROVIDER | Set to lemon-squeezy | - |
LEMON_SQUEEZY_SECRET_KEY | API key | Lemon Squeezy > Settings > API |
LEMON_SQUEEZY_STORE_ID | Your store ID | Lemon Squeezy > Settings > Store |
LEMON_SQUEEZY_SIGNING_SECRET | Webhook signing secret | Lemon Squeezy > Settings > Webhooks |
Email Configuration
Using Resend
| Variable | Value |
|---|---|
MAILER_PROVIDER | resend |
EMAIL_SENDER | noreply@yourdomain.com |
RESEND_API_KEY | Your Resend API key |
Using Nodemailer (SMTP)
| Variable | Description |
|---|---|
MAILER_PROVIDER | nodemailer |
EMAIL_SENDER | noreply@yourdomain.com |
EMAIL_HOST | SMTP host (e.g., smtp.sendgrid.net) |
EMAIL_PORT | SMTP port (usually 587 or 465) |
EMAIL_USER | SMTP username |
EMAIL_PASSWORD | SMTP password or API key |
EMAIL_TLS | Enable TLS (true or false) |
CMS Configuration
| Variable | Description |
|---|---|
CMS_CLIENT | CMS provider: keystatic or wordpress |
Captcha Protection (Optional)
| Variable | Description |
|---|---|
NEXT_PUBLIC_CAPTCHA_SITE_KEY | Cloudflare Turnstile site key |
CAPTCHA_SECRET_TOKEN | Cloudflare Turnstile secret key |
Monitoring (Optional)
| Variable | Description |
|---|---|
CONTACT_EMAIL | Email for receiving contact form submissions |
LOGGER | Logger type: pino (default) or console |
Environment Variable Groups
Minimum Required for Deployment
These are the absolute minimum variables needed for a working deployment:
# ApplicationNEXT_PUBLIC_SITE_URL=https://yourdomain.comNEXT_PUBLIC_PRODUCT_NAME=MyApp# SupabaseNEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.coNEXT_PUBLIC_SUPABASE_PUBLIC_KEY=eyJ...SUPABASE_SECRET_KEY=eyJ...# Billing (Stripe example)NEXT_PUBLIC_BILLING_PROVIDER=stripeNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...STRIPE_SECRET_KEY=sk_live_...STRIPE_WEBHOOK_SECRET=whsec_...# EmailMAILER_PROVIDER=resendEMAIL_SENDER=noreply@yourdomain.comRESEND_API_KEY=re_...# WebhooksSUPABASE_DB_WEBHOOK_SECRET=your-secretFull Production Configuration
Here's a complete example with all common variables:
# ============================================# APPLICATION# ============================================NEXT_PUBLIC_SITE_URL=https://yourdomain.comNEXT_PUBLIC_PRODUCT_NAME=MyAppNEXT_PUBLIC_SITE_TITLE=MyApp - Build SaaS FasterNEXT_PUBLIC_SITE_DESCRIPTION=The complete SaaS starter kitNEXT_PUBLIC_DEFAULT_THEME_MODE=lightNEXT_PUBLIC_DEFAULT_LOCALE=en# ============================================# AUTHENTICATION# ============================================NEXT_PUBLIC_AUTH_PASSWORD=trueNEXT_PUBLIC_AUTH_MAGIC_LINK=false# ============================================# FEATURES# ============================================NEXT_PUBLIC_ENABLE_THEME_TOGGLE=trueNEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_DELETION=trueNEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING=trueNEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS=trueNEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_DELETION=trueNEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_BILLING=trueNEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_CREATION=trueNEXT_PUBLIC_ENABLE_NOTIFICATIONS=trueNEXT_PUBLIC_REALTIME_NOTIFICATIONS=false# ============================================# SUPABASE# ============================================NEXT_PUBLIC_SUPABASE_URL=https://yourproject.supabase.coNEXT_PUBLIC_SUPABASE_PUBLIC_KEY=eyJhbGciOiJI...SUPABASE_SECRET_KEY=eyJhbGciOiJI...SUPABASE_DB_WEBHOOK_SECRET=your-webhook-secret# ============================================# BILLING (Stripe)# ============================================NEXT_PUBLIC_BILLING_PROVIDER=stripeNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...STRIPE_SECRET_KEY=sk_live_...STRIPE_WEBHOOK_SECRET=whsec_...# ============================================# EMAIL# ============================================MAILER_PROVIDER=resendEMAIL_SENDER=noreply@yourdomain.comRESEND_API_KEY=re_...# ============================================# CMS# ============================================CMS_CLIENT=keystatic# ============================================# OPTIONAL# ============================================CONTACT_EMAIL=support@yourdomain.comLOGGER=pinoBuild-Time vs Runtime Variables
MakerKit uses Zod schemas to validate environment variables. Understanding when validation occurs helps debug issues:
Build-Time Validation
Variables prefixed with NEXT_PUBLIC_ are embedded at build time. If missing:
- Build fails with a clear error message
- You must add the variable and rebuild
Runtime Validation
Server-only variables (without NEXT_PUBLIC_ prefix) are validated when the server starts. If missing:
- The application may start but fail when accessing certain features
- Check server logs for validation errors
Secrets Management
What Counts as a Secret
These variables contain sensitive data and must be protected:
SUPABASE_SECRET_KEYSTRIPE_SECRET_KEYSTRIPE_WEBHOOK_SECRETLEMON_SQUEEZY_SECRET_KEYLEMON_SQUEEZY_SIGNING_SECRETSUPABASE_DB_WEBHOOK_SECRETRESEND_API_KEYEMAIL_PASSWORDCAPTCHA_SECRET_TOKEN
Best Practices
- Never commit secrets: Use
.gitignoreto exclude.env*.localfiles - Use hosting provider secrets: Vercel, Cloudflare, etc. have secure environment variable storage
- Rotate compromised secrets: If a secret is exposed, regenerate it immediately
- Limit access: Only give team members access to secrets they need
Platform-Specific Notes
Vercel
- Add variables in Project Settings > Environment Variables
- Separate variables by environment (Production, Preview, Development)
- Use Vercel's sensitive variable feature for secrets
Cloudflare
- Add variables to
.dev.varsfor local development - Use Wrangler secrets for production:
wrangler secret put VARIABLE_NAME - Some variables may need to be in
wrangler.tomlfor build-time access
Docker
- Pass variables via
--env-fileflag - Never bake secrets into Docker images
- Use Docker secrets or external secret managers for production
Troubleshooting
"Required environment variable X is missing"
The variable isn't set in your hosting provider. Add it and redeploy.
"Invalid value for environment variable X"
The variable value doesn't match the expected format. Check:
- URLs should start with
https://and have no trailing slash - Boolean values should be
trueorfalse(not"true") - Provider names must match exactly (
stripe, notStripe)
Variables work locally but not in production
- Verify variables are set in your hosting provider (not just
.env.local) - Check for typos in variable names
- Ensure
NEXT_PUBLIC_prefix is correct for client-side variables - Redeploy after adding variables (Vercel caches builds)
Secrets appearing in client-side code
Only variables with NEXT_PUBLIC_ prefix should be in client code. If server-only secrets appear:
- Check you're not importing server modules in client components
- Verify the variable name doesn't have
NEXT_PUBLIC_prefix - Review your bundle with
next build --analyze
Frequently Asked Questions
Why does MakerKit validate environment variables at build time?
What's the difference between NEXT_PUBLIC_ and regular variables?
How do I add a new environment variable?
Can I use different variables for staging and production?
What if I accidentally commit secrets to Git?
Next Steps
- Deployment Checklist: Complete deployment steps
- Vercel Deployment: Deploy to Vercel with CI/CD
- Supabase Configuration: Set up Supabase with migrations and RLS