Environment Variables
Find which settings you need now, where to put them, and what to configure before launch.
Use this page when you need to add a service or prepare a deployment. You do not need to fill every integration key to explore the local app with seed accounts.
Before You Start
Complete Quick Start. Init creates the local files and signing secrets for you. Keep your app's .env.example open as the field inventory, but put real values in .env.local, not the example file.
What to Configure Now
| Stage | Configure |
|---|---|
| First local sign-in | Generated auth/newsletter secrets; local D1; seed accounts |
| Brand customization | Default locale, public support address, local site origin |
| Test email, OAuth, payment, or upload | Only the credentials for the feature you are testing |
| Deploy | Production origin, database/bindings, stable signing secrets, real mail, and every enabled integration |
Real email requires Cloudflare credentials. Missing integrations can fail when used even though the homepage and seed login work. Product display settings live in packages/config/src/index.ts; credentials are environment values.
Application Environment
| Configuration | Web Cloudflare |
|---|---|
| Local files | apps/web-cloudflare/.env.local; .dev.vars for Worker preview |
| Database | D1 binding DB; no DATABASE_URL |
| Storage | Native R2_BUCKET; no S3 keys |
MAIL_PROVIDER=cloudflare |
pnpm muse:init --yes --app cloudflare
pnpm db:seed:d1
pnpm dev:cfInit preserves existing values and generates missing signing secrets. Do not use --force on a configured environment. The CLI uses --app cloudflare; the app directory is web-cloudflare.
Core Variables
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_SITE_URL | Origin, links, and OAuth callbacks; use the real production HTTPS origin |
NEXT_PUBLIC_DEFAULT_LOCALE | Default en or zh |
NEXT_PUBLIC_EMAIL_SUPPORT | Public support address |
BETTER_AUTH_SECRET | Session signing; missing in production runtime is a hard error |
BETTER_AUTH_TRUSTED_ORIGINS | Comma-separated additional trusted origins |
BETTER_AUTH_ENABLE_TEST_UTILS | Automated test helpers; keep false normally |
GITHUB_CLIENT_ID/SECRET, GOOGLE_CLIENT_ID/SECRET | Optional OAuth; missing credentials hide the provider |
NEXT_PUBLIC_GOOGLE_ONE_TAP_CLIENT_ID | Optional Google One Tap |
NEXT_PUBLIC_TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEY | Both required to enable captcha |
MAIL_FROM, MAIL_FROM_NEWSLETTER, CONTACT_FORM_TO | Transactional/newsletter sender and contact inbox |
NEWSLETTER_TOKEN_SECRET | Unsubscribe HMAC signing, without an auth-secret fallback |
MUSE_SEED_EMAIL, MUSE_SEED_PASSWORD | Local seed account domain and password, not production provisioning |
Mail Credentials
Set MAIL_PROVIDER=cloudflare, CLOUDFLARE_ACCOUNT_ID, and CLOUDFLARE_API_TOKEN, and verify your sending domain. Workers production rejects resend and console. Missing credentials can warn at startup but still break OTP delivery.
Billing
BILLING_DEFAULT_GATEWAY accepts muse_creem, muse_stripe, muse_dodo, or muse_waffo. Empty falls back across configured gateways. Never put secrets in NEXT_PUBLIC_*.
- Creem:
CREEM_API_KEY,CREEM_WEBHOOK_SECRET,CREEM_ENVIRONMENT - Stripe:
STRIPE_SECRET_KEY,STRIPE_WEBHOOK_SECRET - Dodo:
DODO_PAYMENTS_API_KEY,DODO_PAYMENTS_WEBHOOK_SECRET,DODO_PAYMENTS_ENVIRONMENT - Waffo:
WAFFO_MERCHANT_ID,WAFFO_PRIVATE_KEY,WAFFO_ENVIRONMENT; optionalWAFFO_DEBUG,WAFFO_CUSTOMER_PORTAL_URL - Products:
<GATEWAY>_PRODUCT_ID_PRO_MONTHLY,<GATEWAY>_PRODUCT_ID_PRO_YEARLY,<GATEWAY>_PRODUCT_ID_LIFETIME, withCREEM,STRIPE,DODO, orWAFFOprefixes
Store a Waffo private key on one line with \n for newlines. It signs API requests; webhooks use SDK verification, without a separate Waffo webhook secret. See gateway guides for sandbox/production environment values.
Worker Bindings
Declare D1 DB and R2 R2_BUCKET in apps/web-cloudflare/wrangler.jsonc. Run pnpm cf:typegen after binding changes. Assets use session-protected /files/avatars/*. See storage.
Build and Runtime Boundaries
NEXT_PUBLIC_* values are inlined into clients and require a rebuild when changed. Inject server secrets on the target platform. Do not copy a whole local file containing seed credentials and development URLs into production.
src/env/readiness.ts collects errors/warnings, throws hard errors in production runtime, and does not abort the build phase for those same hard errors. Use SKIP_ENV_VALIDATION=true only for necessary secretless CI builds, never to hide missing production config. A successful build does not prove auth, mail, or database readiness.
Keep package ./env, the selected app schema, and .env.example aligned when adding variables. Platform differences are intentional. Never commit .env.local, .dev.vars, or real secrets.
Check Your Result
Restart the dev server after editing environment files. Workers local changes also need .dev.vars; production needs host secrets. Open the app and test the specific integration you enabled, such as a delivered email or sandbox checkout. Rebuild after changing NEXT_PUBLIC_* on a deployed app.
Common Problems
| Problem | Fix |
|---|---|
| Local settings changed but behavior did not | Check the selected app's file and restart its server |
| Workers preview differs from Next dev | Compare .dev.vars and bindings with .env.local, then test pnpm cf:preview |
| Live links still point at localhost | Correct the build-time public origin and rebuild |
| Production starts but a feature fails | Read server/provider errors and check that feature's required values, not just build success |
Follow Email, Payments, and Storage for service-specific walkthroughs.