Version 3 of the kit: - Radix UI replaced with Base UI (using the Shadcn UI patterns) - next-intl replaces react-i18next - enhanceAction deprecated; usage moved to next-safe-action - main layout now wrapped with [locale] path segment - Teams only mode - Layout updates - Zod v4 - Next.js 16.2 - Typescript 6 - All other dependencies updated - Removed deprecated Edge CSRF - Dynamic Github Action runner
370 lines
13 KiB
Plaintext
370 lines
13 KiB
Plaintext
---
|
|
status: "published"
|
|
title: "Production Environment Variables for Next.js Supabase SaaS"
|
|
label: "Environment Variables"
|
|
description: "Complete reference for generating and configuring production environment variables in your MakerKit Next.js Supabase application."
|
|
order: 2
|
|
---
|
|
|
|
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:
|
|
|
|
```bash
|
|
pnpm turbo gen env
|
|
```
|
|
|
|
This command:
|
|
1. Prompts you for each variable value
|
|
2. Uses defaults from your existing `.env` files when available
|
|
3. Creates a file at `turbo/generators/templates/env/.env.local`
|
|
|
|
Copy the contents of this file to your hosting provider's environment variable settings.
|
|
|
|
{% alert type="warning" title="Never commit this file" %}
|
|
The generated `.env.local` contains secrets. It's git-ignored by default, but verify it's not being tracked.
|
|
{% /alert %}
|
|
|
|
---
|
|
|
|
## Validate Environment Variables
|
|
|
|
After generating or manually setting variables, validate them:
|
|
|
|
```bash
|
|
turbo gen validate-env
|
|
```
|
|
|
|
This 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` |
|
|
|
|
{% alert type="warning" title="Keep secrets private" %}
|
|
`SUPABASE_SECRET_KEY` bypasses Row Level Security. Never expose it in client-side code.
|
|
{% /alert %}
|
|
|
|
### 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:
|
|
|
|
```bash
|
|
# Application
|
|
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
|
|
NEXT_PUBLIC_PRODUCT_NAME=MyApp
|
|
|
|
# Supabase
|
|
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
|
|
NEXT_PUBLIC_SUPABASE_PUBLIC_KEY=eyJ...
|
|
SUPABASE_SECRET_KEY=eyJ...
|
|
|
|
# Billing (Stripe example)
|
|
NEXT_PUBLIC_BILLING_PROVIDER=stripe
|
|
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
|
|
STRIPE_SECRET_KEY=sk_live_...
|
|
STRIPE_WEBHOOK_SECRET=whsec_...
|
|
|
|
# Email
|
|
MAILER_PROVIDER=resend
|
|
EMAIL_SENDER=noreply@yourdomain.com
|
|
RESEND_API_KEY=re_...
|
|
|
|
# Webhooks
|
|
SUPABASE_DB_WEBHOOK_SECRET=your-secret
|
|
```
|
|
|
|
### Full Production Configuration
|
|
|
|
Here's a complete example with all common variables:
|
|
|
|
```bash
|
|
# ============================================
|
|
# APPLICATION
|
|
# ============================================
|
|
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
|
|
NEXT_PUBLIC_PRODUCT_NAME=MyApp
|
|
NEXT_PUBLIC_SITE_TITLE=MyApp - Build SaaS Faster
|
|
NEXT_PUBLIC_SITE_DESCRIPTION=The complete SaaS starter kit
|
|
NEXT_PUBLIC_DEFAULT_THEME_MODE=light
|
|
NEXT_PUBLIC_DEFAULT_LOCALE=en
|
|
|
|
# ============================================
|
|
# AUTHENTICATION
|
|
# ============================================
|
|
NEXT_PUBLIC_AUTH_PASSWORD=true
|
|
NEXT_PUBLIC_AUTH_MAGIC_LINK=false
|
|
|
|
# ============================================
|
|
# FEATURES
|
|
# ============================================
|
|
NEXT_PUBLIC_ENABLE_THEME_TOGGLE=true
|
|
NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_DELETION=true
|
|
NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING=true
|
|
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS=true
|
|
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_DELETION=true
|
|
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_BILLING=true
|
|
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_CREATION=true
|
|
NEXT_PUBLIC_ENABLE_NOTIFICATIONS=true
|
|
NEXT_PUBLIC_REALTIME_NOTIFICATIONS=false
|
|
|
|
# ============================================
|
|
# SUPABASE
|
|
# ============================================
|
|
NEXT_PUBLIC_SUPABASE_URL=https://yourproject.supabase.co
|
|
NEXT_PUBLIC_SUPABASE_PUBLIC_KEY=eyJhbGciOiJI...
|
|
SUPABASE_SECRET_KEY=eyJhbGciOiJI...
|
|
SUPABASE_DB_WEBHOOK_SECRET=your-webhook-secret
|
|
|
|
# ============================================
|
|
# BILLING (Stripe)
|
|
# ============================================
|
|
NEXT_PUBLIC_BILLING_PROVIDER=stripe
|
|
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
|
|
STRIPE_SECRET_KEY=sk_live_...
|
|
STRIPE_WEBHOOK_SECRET=whsec_...
|
|
|
|
# ============================================
|
|
# EMAIL
|
|
# ============================================
|
|
MAILER_PROVIDER=resend
|
|
EMAIL_SENDER=noreply@yourdomain.com
|
|
RESEND_API_KEY=re_...
|
|
|
|
# ============================================
|
|
# CMS
|
|
# ============================================
|
|
CMS_CLIENT=keystatic
|
|
|
|
# ============================================
|
|
# OPTIONAL
|
|
# ============================================
|
|
CONTACT_EMAIL=support@yourdomain.com
|
|
LOGGER=pino
|
|
```
|
|
|
|
---
|
|
|
|
## Build-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_KEY`
|
|
- `STRIPE_SECRET_KEY`
|
|
- `STRIPE_WEBHOOK_SECRET`
|
|
- `LEMON_SQUEEZY_SECRET_KEY`
|
|
- `LEMON_SQUEEZY_SIGNING_SECRET`
|
|
- `SUPABASE_DB_WEBHOOK_SECRET`
|
|
- `RESEND_API_KEY`
|
|
- `EMAIL_PASSWORD`
|
|
- `CAPTCHA_SECRET_TOKEN`
|
|
|
|
### Best Practices
|
|
|
|
1. **Never commit secrets**: Use `.gitignore` to exclude `.env*.local` files
|
|
2. **Use hosting provider secrets**: Vercel, Cloudflare, etc. have secure environment variable storage
|
|
3. **Rotate compromised secrets**: If a secret is exposed, regenerate it immediately
|
|
4. **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.vars` for local development
|
|
- Use Wrangler secrets for production: `wrangler secret put VARIABLE_NAME`
|
|
- Some variables may need to be in `wrangler.toml` for build-time access
|
|
|
|
### Docker
|
|
|
|
- Pass variables via `--env-file` flag
|
|
- 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 `true` or `false` (not `"true"`)
|
|
- Provider names must match exactly (`stripe`, not `Stripe`)
|
|
|
|
### Variables work locally but not in production
|
|
|
|
1. Verify variables are set in your hosting provider (not just `.env.local`)
|
|
2. Check for typos in variable names
|
|
3. Ensure `NEXT_PUBLIC_` prefix is correct for client-side variables
|
|
4. 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:
|
|
1. Check you're not importing server modules in client components
|
|
2. Verify the variable name doesn't have `NEXT_PUBLIC_` prefix
|
|
3. Review your bundle with `next build --analyze`
|
|
|
|
---
|
|
|
|
{% faq
|
|
title="Frequently Asked Questions"
|
|
items=[
|
|
{"question": "Why does MakerKit validate environment variables at build time?", "answer": "Build-time validation catches configuration errors before deployment. Missing a critical variable like STRIPE_SECRET_KEY would otherwise cause runtime errors that are harder to debug. Zod schemas ensure all required variables are present and correctly formatted."},
|
|
{"question": "What's the difference between NEXT_PUBLIC_ and regular variables?", "answer": "Variables prefixed with NEXT_PUBLIC_ are embedded in the client-side JavaScript bundle and visible to users. Never use this prefix for secrets. Regular variables are only available server-side and stay secure. This is a Next.js convention."},
|
|
{"question": "How do I add a new environment variable?", "answer": "Add the variable to your hosting provider's settings, then redeploy. For client-side variables (NEXT_PUBLIC_), you must rebuild since they're embedded at build time. For server-only variables, a restart is usually sufficient."},
|
|
{"question": "Can I use different variables for staging and production?", "answer": "Yes. Most hosting providers support environment-specific variables. Create separate Supabase projects for each environment, generate variables for each, and configure your hosting provider to use the right set based on the deployment environment."},
|
|
{"question": "What if I accidentally commit secrets to Git?", "answer": "Immediately rotate all exposed credentials. Generate new API keys for Supabase, Stripe, and any other affected services. Consider using git-secrets or similar tools to prevent future accidental commits. Review Git history and consider rewriting it if the repo is private."}
|
|
]
|
|
/%}
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
- [Deployment Checklist](/docs/next-supabase-turbo/going-to-production/checklist): Complete deployment steps
|
|
- [Vercel Deployment](/docs/next-supabase-turbo/going-to-production/vercel): Deploy to Vercel with CI/CD
|
|
- [Supabase Configuration](/docs/next-supabase-turbo/going-to-production/supabase): Set up Supabase with migrations and RLS
|