# Environment Setup

> Configure production environment variables and secrets.

*Canonical: https://makerkit.dev/docs/nextjs-prisma/going-to-production/environment-setup*

---

Production environment setup involves configuring database connections, authentication secrets, billing provider credentials, S3-compatible storage, and email services. Use the Dev Tools application to generate a complete `.env.production.local` file, or manually set variables in your hosting platform's dashboard. All `NEXT_PUBLIC_*` variables must be set before building—they're embedded at build time, not runtime.

This guide covers the essential variables and how to set them up.

## Using Dev Tools (Recommended)

The easiest way to configure production environment variables is using the Dev Tools application:

1. Run Dev Tools: `pnpm --filter dev-tool dev`
2. Open `http://localhost:3010/variables`
3. Select **Production** mode from the dropdown
4. Fill in missing variables (highlighted in red)
5. Click **Copy** to copy all variables
6. Paste into your hosting provider's environment settings

For more details, see the [Dev Tools documentation](../dev-tools/environment-variables).

## Required Environment Variables

### Database

```bash
DATABASE_URL=postgresql://user:password@host:5432/database
```

Your PostgreSQL connection string. Get this from your database provider (Neon, PlanetScale, Supabase, Railway, etc.).

### Site URL

```bash
NEXT_PUBLIC_SITE_URL=https://your-domain.com
```

Your production domain. Must match exactly - used for authentication callbacks, emails, and absolute URLs.

### Authentication Secret

```bash
BETTER_AUTH_SECRET=your-random-secret-string
```

A random secret string used to sign authentication tokens and cookies. Generate a secure random string (at least 32 characters). You can generate one using:

```bash
openssl rand -base64 32
```

{% alert type="warning" title="Security Critical" %}
Use a unique, randomly generated secret for production. Never reuse your development secret or share it publicly.
{% /alert %}

### Billing Provider

```bash
NEXT_PUBLIC_BILLING_PROVIDER=stripe
```

Set to `stripe` or `polar` depending on your billing provider.

**For Stripe:**
```bash
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
```

**For Polar:**
```bash
POLAR_ACCESS_TOKEN=...
```

### Storage (S3-Compatible)

```bash
STORAGE_BASE_URL=https://your-bucket-endpoint.com
STORAGE_S3_ACCESS_KEY_ID=...
STORAGE_S3_SECRET_ACCESS_KEY=...
STORAGE_S3_BUCKET=your-bucket-name
STORAGE_S3_REGION=us-east-1
```

Required for file uploads. Works with AWS S3, Cloudflare R2, Railway Storage, or any S3-compatible provider.

### Email

```bash
EMAIL_SENDER=noreply@your-domain.com
```

Plus your email provider credentials. Here's an example for Resend (recommended):

```bash
EMAIL_SENDER=noreply@your-domain.com
RESEND_API_KEY=re_...
```

For other providers:

**Postmark:**
```bash
POSTMARK_API_KEY=...
```

**SendGrid:**
```bash
SENDGRID_API_KEY=...
```

**SMTP (generic):**
```bash
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=...
SMTP_PASSWORD=...
```

See the [Email Configuration](../emails/configuration) guide for detailed setup instructions.

## Optional Environment Variables

### OAuth Providers

If you enabled social login during development:

**Google:**
```bash
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
```

**GitHub:**
```bash
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
```

{% alert type="warning" title="OAuth Redirect URLs" %}
Remember to add your production domain to the OAuth provider's allowed redirect URLs.
{% /alert %}

### Feature Flags

```bash
NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING=true
```

Match these to your development configuration. For organization/team settings, use `NEXT_PUBLIC_ACCOUNT_MODE` (see [Account Modes](../configuration/account-modes)).

## Environment Variables Reference

| Variable | Required | Description |
|----------|----------|-------------|
| `DATABASE_URL` | Yes | PostgreSQL connection string |
| `NEXT_PUBLIC_SITE_URL` | Yes | Production domain URL |
| `BETTER_AUTH_SECRET` | Yes | Authentication token signing secret |
| `NEXT_PUBLIC_BILLING_PROVIDER` | Yes | `stripe` or `polar` |
| `STRIPE_SECRET_KEY` | If Stripe | Stripe secret key |
| `STRIPE_WEBHOOK_SECRET` | If Stripe | Stripe webhook signing secret |
| `POLAR_ACCESS_TOKEN` | If Polar | Polar API token |
| `STORAGE_BASE_URL` | Yes | S3 endpoint URL |
| `STORAGE_S3_ACCESS_KEY_ID` | Yes | S3 access key |
| `STORAGE_S3_SECRET_ACCESS_KEY` | Yes | S3 secret key |
| `STORAGE_S3_BUCKET` | Yes | S3 bucket name |
| `STORAGE_S3_REGION` | Yes | S3 region |
| `EMAIL_SENDER` | Yes | From email address |

For the complete list, see the [Environment Variables Reference](../configuration/environment-variables).

## Best Practices

1. **Never commit secrets** - Use `.env.local` for local development, never commit to git
2. **Use different values per environment** - Don't reuse development keys in production
3. **Validate before deploying** - Use Dev Tools to check all required variables are set
4. **Rotate secrets regularly** - Especially after team member changes
5. **Use your provider's secret management** - Most hosting platforms encrypt environment variables

## Common Mistakes

### Wrong site URL
`NEXT_PUBLIC_SITE_URL` must match your actual domain exactly, including `https://`.

**Symptoms:** Authentication callbacks fail, email links point to wrong domain, OAuth redirects break.

**Fix:** Set `NEXT_PUBLIC_SITE_URL=https://your-actual-domain.com` (no trailing slash). Redeploy after changing since it's a `NEXT_PUBLIC_` variable.

### Missing webhook secrets
Billing won't work without webhook configuration.

**Symptoms:** Subscriptions don't activate after payment, webhook endpoint returns 401/403 errors.

**Fix:** Copy the webhook signing secret from your Stripe Dashboard → Developers → Webhooks → Your endpoint → Signing secret. Set as `STRIPE_WEBHOOK_SECRET`.

### Development keys in production
Stripe test keys (`sk_test_*`) won't process real payments.

**Symptoms:** Payments work in test mode but fail with real cards, or test data appears in production.

**Fix:** In Stripe Dashboard, toggle to "Live mode" and copy the live secret key (`sk_live_*`). Update `STRIPE_SECRET_KEY` and create a new webhook endpoint for production.

### Typos in variable names
Environment variables are case-sensitive and must match exactly.

**Symptoms:** Application crashes on startup with "missing required environment variable" errors.

**Fix:** Double-check spelling, especially for `NEXT_PUBLIC_` prefix. Use the Dev Tools validator to catch missing or misnamed variables before deploying.

### `NEXT_PUBLIC_*` variables added after build
Variables prefixed with `NEXT_PUBLIC_` are embedded at build time, not read at runtime.

**Symptoms:** Client-side code shows `undefined` for environment variables that are definitely set.

**Fix:** Set all `NEXT_PUBLIC_*` variables in your hosting platform's settings, then trigger a new build and deployment.
