Environment variables
Every secret and Wrangler variable the web Worker, the admin Worker and the jobs Worker read, where it lives and where to get the value.
This kit has no browser-exposed environment variables. Everything below is read inside a Cloudflare Worker and never shipped to the client. Configuration is split in two places:
- Secrets go in
.dev.varsfiles locally and in Worker secrets (wrangler secret put) when deployed. The templates areapps/web/.env.example,apps/admin/.env.exampleandapps/jobs/.env.example. - Non-secret variables live in the
varsblocks ofapps/web/wrangler.jsonc,apps/admin/wrangler.jsoncandapps/jobs/wrangler.jsonc, once per environment (top level for local, thenenv.previewandenv.production).
Copy the templates only when the .dev.vars files are missing. See installation for the commands.
Keep secrets out of vars
The repository's runbook says Unkey, OpenAI, Stripe, Resend and Cloudflare credentials must never appear in vars, client loader data, logs or Queue payloads. Use Worker secrets for anything in the secrets tables below.
Web Worker secrets
File: apps/web/.dev.vars (template apps/web/.env.example). Deployed: pnpm --filter @nextbase/web exec wrangler secret put <NAME> --env <preview|production>.
| Variable | Required | What it is for | Where to get the value |
|---|---|---|---|
BETTER_AUTH_SECRET | Required outside localhost | Signs Better Auth sessions and tokens. The Worker refuses to start on a non-local SITE_URL without it. Locally a development fallback is used. | Generate at least 32 random characters. |
ANALYTICS_PSEUDONYM_SECRET | Optional, recommended | HMAC key that pseudonymizes user IDs in product_analytics_events. Falls back to the Better Auth secret. Rotating it breaks the link to earlier pseudonyms on purpose. | Generate an independent random secret. |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | Optional | Enables the Google sign-in button. Both must be set. | Google Cloud OAuth client. |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET | Optional | Enables the GitHub sign-in button. Both must be set. | GitHub OAuth app. |
TWITTER_CLIENT_ID / TWITTER_CLIENT_SECRET | Optional | Enables the Twitter sign-in button. Both must be set. | Twitter developer app. |
STRIPE_SECRET_KEY | Optional | Stripe API access for Checkout, the billing portal and catalog reads. | Stripe dashboard, API keys. |
STRIPE_WEBHOOK_SECRET | Optional | Verifies the raw body of POST /api/stripe/webhooks. | Stripe webhook endpoint signing secret. |
OPENAI_API_KEY | Optional | AI SDK streaming chat at /api/chat. | OpenAI API keys. |
UNKEY_ROOT_KEY | Optional | Creates and verifies developer API keys for /api/v1/me and /user/api-keys. | Unkey dashboard, root key. |
UNKEY_API_ID | Optional | The Unkey API the keys belong to. Treated as public and also present in Wrangler vars. | Unkey dashboard, API ID. |
API_CORS_ORIGINS | Optional | Comma-separated exact origins allowed to call the public API. Default http://localhost:5173. Also set in Wrangler vars. | Your deployed origin, for example https://app.example.com. |
RESEND_API_KEY, CLOUDFLARE_API_TOKEN and CLOUDFLARE_ZONE_ID are not in the web template and not in the web Env type. Email delivery and cache purges happen in the jobs Worker, so those secrets belong only there.
Admin Worker secrets and local values
File: apps/admin/.dev.vars (template apps/admin/.env.example). Deployed: pnpm --filter @nextbase/admin exec wrangler secret put <NAME> --env <preview|production>.
| Variable | Required | What it is for | Where to get the value |
|---|---|---|---|
SITE_URL | In the template | The admin app's own origin, http://localhost:5174 locally. Also set in the admin Wrangler vars. | Your admin origin. |
WEB_APP_URL | In the template | The customer web app's origin, http://localhost:5173 locally. Used to build the impersonation handoff and the links in magic links issued from the admin console. Also set in the admin Wrangler vars. | Your web origin. |
ADMIN_APP | In the template, true | Turns the shared code into the administrator app: the Admin MCP audience, the nextbase-admin cookie prefix. Also set in the admin Wrangler vars. | Keep true. |
BETTER_AUTH_SECRET | Required outside localhost | An independent secret for the admin app's own Better Auth instance. Never reuse the web Worker's value. | Generate at least 32 random characters. |
The admin Worker shares the web app's code, so the optional web secrets (social provider keys, Stripe, OpenAI, Unkey) are read there too when you set them. Its login page shows a social button only for a provider configured on the admin Worker.
Jobs Worker secrets
File: apps/jobs/.dev.vars (template apps/jobs/.env.example). Deployed: pnpm --filter @nextbase/jobs exec wrangler secret put <NAME> --env <preview|production>.
| Variable | Required | What it is for | Where to get the value |
|---|---|---|---|
RESEND_API_KEY | Required in production | Sends queued email.send jobs through Resend. When empty, emails are written to the dev_email_inbox D1 table instead. | Resend dashboard. |
STRIPE_SECRET_KEY | Optional | Lets webhook processing re-fetch current Stripe objects before updating D1. | Stripe dashboard, API keys. |
UNKEY_ROOT_KEY | Optional | Revokes provider API keys through the outbox after a local revocation. | Unkey dashboard, root key. |
CLOUDFLARE_API_TOKEN | Required in deployed environments | Zone-scoped token used by cache.purge jobs. Missing credentials are tolerated only for localhost. | Cloudflare API token with cache purge for one zone. |
CLOUDFLARE_ZONE_ID | Required in deployed environments | The zone whose cache is purged. | Cloudflare dashboard, zone overview. |
Web Worker vars
Set in apps/web/wrangler.jsonc for each environment.
| Variable | Local default | What it is for |
|---|---|---|
APP_NAME | Nextbase | Shown in the UI and used as the Better Auth app name and in email subjects. |
SITE_URL | http://localhost:5173 | The canonical origin. Better Auth baseURL and trusted origin, cross-origin mutation check, telemetry origin check, and the origin of the User MCP endpoint. |
ADMIN_APP_URL | http://localhost:5174 | The admin app's origin. /stop-impersonating sends the administrator back to ADMIN_APP_URL + /app-admin/users. |
ADMIN_EMAIL | admin@nextbase.local | Support address shown in emails. |
UNKEY_API_ID | empty | Public Unkey API identifier for developer keys. |
API_CORS_ORIGINS | http://localhost:5173 | Exact-origin CORS allowlist for /api/v1/me. Keep preview and production lists separate and never use *. |
Admin Worker vars
Set in apps/admin/wrangler.jsonc for each environment.
| Variable | Local default | What it is for |
|---|---|---|
APP_NAME | Nextbase | Same role as on the web Worker. |
SITE_URL | http://localhost:5174 | The admin origin. Better Auth baseURL, same-origin check for mutations, and the origin of the Admin MCP endpoint and its OAuth issuer. |
ADMIN_APP | true | Marks this Worker as the administrator app. The MCP handler serves only the admin audience when it is true. |
WEB_APP_URL | http://localhost:5173 | The web origin. Impersonation redirects to WEB_APP_URL + /auth/impersonate; cache purges and magic links issued from the console use it as the public site origin. Impersonation fails if it is missing. |
ADMIN_EMAIL | admin@nextbase.local | Support address. |
UNKEY_API_ID | empty | Present for the shared code; developer API keys are managed on the web app. |
API_CORS_ORIGINS | http://localhost:5173 | Present for the shared code; the public API route /api/v1/me exists only on the web app. |
Jobs Worker vars
Set in apps/jobs/wrangler.jsonc for each environment.
| Variable | Local default | What it is for |
|---|---|---|
SITE_URL | http://localhost:5173 | Used to build links inside emails and jobs. Keep it identical to the web Worker's value. |
ADMIN_EMAIL | admin@nextbase.local | The support address in templates, and the Resend from address when MAIL_FROM is not set. |
MAIL_FROM | not set | Optional. When present, apps/jobs/src/handlers.ts uses it as the Resend sender, so the sender can be a verified address such as Nextbase <no-reply@example.com> while ADMIN_EMAIL stays a real mailbox. The shipped env.production block sets it. |
Bindings, not variables
These are configured in the Wrangler files and are not env vars you type. Keep the binding names exactly as they are; the code depends on them.
| Binding | Type | Used by |
|---|---|---|
APP_DB | D1 database | Web, admin and jobs. Must point at the same database in each environment. |
PUBLIC_MEDIA, PRIVATE_MEDIA | R2 buckets | Web, admin and jobs |
JOBS | Queue producer | Web, admin and jobs; the jobs Worker is also the consumer with a -dlq dead-letter queue |
REALTIME | Durable Object RealtimeRoom | Defined in web; admin and jobs reference it through script_name |
AUTH_RATE_LIMITER | Durable Object AuthRateLimiter | Defined in web; admin references it through script_name |
CF_VERSION_METADATA | Worker version metadata | All three Workers, for release IDs in logs |
After changing a binding or a var, run pnpm cf:typegen to regenerate worker-configuration.d.ts in the web and jobs Workers, and pnpm --filter @nextbase/admin cf:typegen for the admin Worker, so the Env types match.
Related
- Authentication for what
BETTER_AUTH_SECRETand the provider keys turn on. - Deployment for the full list of
wrangler secret putcommands. - Emails with Resend for the shared email guidance.