Environment variables
Every environment variable the kit reads, whether it's required, and where its value comes from.
Locally, variables live in .env.local in the project root (never committed). In production, they're set in Vercel under Settings → Environment Variables. npm run setup fills in both.
After changing one: restart npm run dev locally, or redeploy on Vercel.
App
| Variable | Required | Example | What it's for |
|---|---|---|---|
NEXT_PUBLIC_APP_URL | Yes | https://yourdomain.com | The app's public address. Used for sign-in callbacks, email links, the checkout return URL, canonical URLs, share images and the sitemap. No trailing slash. Locally http://localhost:3000 |
BETTER_AUTH_SECRET | Yes | 44 random characters | Signs session cookies. Generate with openssl rand -base64 32. Changing it signs everyone out |
Database
| Variable | Required | Example | What it's for |
|---|---|---|---|
DATABASE_URL | Yes | postgresql://user:pass@ep-….neon.tech/neondb?sslmode=require | Neon Postgres connection string |
Payments (Dodo)
| Variable | Required | Example | What it's for |
|---|---|---|---|
DODO_PAYMENTS_API_KEY | Yes | API key with Write access, from Developer → API Keys | |
DODO_PAYMENTS_ENVIRONMENT | Yes | test_mode or live_mode | Must match the key's mode. Anything other than live_mode means test mode |
DODO_PAYMENTS_WEBHOOK_SECRET | For payments to unlock plans | whsec_… | Signing secret of your webhook endpoint. Setup sets it on Vercel when deploying |
DODO_PRODUCT_<PLAN> | One per plan | pdt_… | Each plan's Dodo product ID. The names come from productEnv in src/config.ts; the example plans use DODO_PRODUCT_STARTER and DODO_PRODUCT_PRO |
Email (Resend)
| Variable | Required | Example | What it's for |
|---|---|---|---|
RESEND_API_KEY | In production, for email sign-in | re_… | Without it: emails print to the terminal locally, and email sign-in is hidden in production |
EMAIL_FROM | With Resend | "Your Product <hello@yourdomain.com>" | Sender. The domain must be verified in Resend. Quote it in .env.local |
Social sign-in
Each provider turns on only when both its variables are set.
| Variable | Required | What it's for |
|---|---|---|
GOOGLE_CLIENT_ID | No | Google OAuth client ID |
GOOGLE_CLIENT_SECRET | No | Google OAuth client secret |
GITHUB_CLIENT_ID | No | GitHub OAuth app client ID |
GITHUB_CLIENT_SECRET | No | GitHub OAuth app client secret |
Repository delivery
Both must be set to turn the feature on. See Selling access to a GitHub repo.
| Variable | Required | Example | What it's for |
|---|---|---|---|
GITHUB_REPO | No | your-org/your-product | The private repository buyers are invited to |
GITHUB_REPO_TOKEN | No | github_pat_… | Fine-grained token with Administration: Read and write on that repository |
Formatting .env.local
# One per line, no spaces around =
DATABASE_URL=postgresql://user:pass@host/db?sslmode=require
# Quote values with spaces or special characters
EMAIL_FROM="Your Product <hello@yourdomain.com>"Common mistakes
- The file must be named exactly
.env.local(not.env.local.txt, which Windows may add when you save from Notepad), in the same folder aspackage.json. - No spaces around
=. - Copy keys in full, without line breaks or trailing spaces.
NEXT_PUBLIC_variables are built into the page. Changing one needs a rebuild, not just a restart, in production.
Never commit secrets
.env.local is in .gitignore. If you ever commit a key by accident, deleting the file isn't enough: it stays in Git history. Rotate the key in the service's dashboard (create a new one, delete the old one).