Nextbase Docs
Nextbase Docs
HomeBlogWelcome to Nextbase v5!Choose your starterGetting startedLocal Supabase per agent
Next.js + NeonInstallationEnvironment variablesAuthenticationAgent connectionsProject structureDeployment
AuthenticationWorkspaces and the admin area
Building with agentsEmails with Resend
TroubleshootingNextbase v5
The 8 starter kitsNext.js + Neon

Environment variables

Every variable in the Next.js + Neon starter's .env.example and apps/admin/.env.local.example, what it does and where the value comes from.

The kit has two example env files. .env.example at the repository root is the one you copy to .env.local; the web app, the database package and the scripts read it. apps/admin/.env.local.example is the admin app's template, copied to apps/admin/.env.local; it only lists the origin variables that differ between the two apps. Both apps validate values with the same zod schema in apps/web/src/lib/env.ts (the admin app resolves @/lib/env to that file), and almost every variable has a local default there, so both apps start with the example files unchanged. apps/web/src/environment.d.ts mirrors the schema for process.env typing.

Variables prefixed with NEXT_PUBLIC_ are bundled into the browser. Everything else is server-only. Never put a secret in a NEXT_PUBLIC_ variable.

"Required" below means the app can't do its job in production without it. Locally the example values work as they are.

Database

VariableRequiredPublicPurposeWhere to get it
DATABASE_URLYesNoConnection string used by both apps at runtime. In production, Neon's pooled endpoint with an application role.Neon project connection details; locally postgresql://nextbase:nextbase@127.0.0.1:35577/nextbase
DATABASE_DIRECT_URLFor migrationsNoConnection string used by pnpm db:migrate and drizzle-kit. In production, Neon's direct endpoint with a migration role. Falls back to DATABASE_URL when unset.Neon project connection details; locally the same as DATABASE_URL
DATABASE_POOL_SIZENoNoMaximum connections in the shared pg.Pool. Defaults to 5. Not in .env.example.Your choice

Auth and app origin

VariableRequiredPublicPurposeWhere to get it
BETTER_AUTH_URLYesNoBetter Auth base URL. Must be the public origin of the app that reads it: the web origin on web, the admin origin on admin.Your deployment URL; locally http://localhost:3000 for web and http://localhost:3001 for admin
BETTER_AUTH_SECRETYesNoSigns sessions and tokens. At least 32 characters. Both apps must use the same value, because the impersonation handoff verifies a token issued by the admin app on the web app.Generate a random string; never reuse the example
BETTER_AUTH_TRUSTED_ORIGINSNoNoComma-separated extra origins Better Auth accepts, on top of BETTER_AUTH_URL and NEXT_PUBLIC_APP_URL. Useful for preview domains.Your extra origins
BETTER_AUTH_ORGANIZATION_LIMITNoNoHow many workspaces one user can create. Defaults to 10. Not in .env.example; .env.test sets 100.Your choice
ENFORCE_PRODUCTION_SECRETSYes in productionNoSet to true so the app fails fast when BETTER_AUTH_SECRET is still the development default.Set true in production
NEXT_PUBLIC_APP_URLYesYesCanonical origin of the app that reads it. Used for links in emails, invitation URLs, trusted origins and as the origin of that app's MCP server: /mcp/user on the web origin, /mcp/admin on the admin origin.Your deployment URL for each app

Independent app origins

Both apps need to know each other's address. These two are at the end of .env.example and in apps/admin/.env.local.example.

VariableRequiredPublicPurposeWhere to get it
NEXT_PUBLIC_WEB_APP_URLYesYesThe customer web origin. The admin app sends impersonation handoffs and customer login links there, and calls its /api/internal/admin-revalidate endpoint after admin mutations.Your web deployment URL; locally http://localhost:3000
NEXT_PUBLIC_ADMIN_APP_URLYesYesThe admin origin. The web app links administrators to <admin origin>/app-admin from the sidebar and returns there when an impersonation session ends.Your admin deployment URL; locally http://localhost:3001

Admin app (apps/admin/.env.local.example)

The admin app is a separate Next.js project, so it reads apps/admin/.env.local rather than the root file. Its template sets the origin variables to the admin address and leaves everything else to the shared defaults.

VariableValue on the admin appPurpose
BETTER_AUTH_URLThe admin origin, locally http://localhost:3001Better Auth base URL for the admin app. Its authentication cookies are host-only, so administrators sign in on the admin host.
NEXT_PUBLIC_APP_URLThe admin originCanonical admin origin; also the origin of the Admin MCP.
NEXT_PUBLIC_SITE_URLThe admin originSite URL for the admin app, as apps/admin/AGENTS.md asks.
NEXT_PUBLIC_WEB_APP_URLThe web origin, locally http://localhost:3000Where impersonation handoffs and login links go.
NEXT_PUBLIC_ADMIN_APP_URLThe admin originSame value as on web.

In production, also give the admin app the same DATABASE_URL, BETTER_AUTH_SECRET, REVALIDATE_SECRET, S3_* and Stripe variables as the web app; it shares the web app's server modules and uses them for marketing CMS uploads and the Stripe catalog sync on the payment gateway page. Locally the defaults in env.ts cover the shared values.

Email

VariableRequiredPublicPurposeWhere to get it
MAIL_FROMYesNoDefault sender, for example Nextbase <hello@yourdomain.com>.A verified sender on your domain
EMAIL_PROVIDERYesNosmtp or resend. Selects the transport in apps/web/src/lib/email/send-email.ts.smtp locally with Mailpit, resend in production
SMTP_HOSTWith smtpNoSMTP host.Locally 127.0.0.1 (Mailpit)
SMTP_PORTWith smtpNoSMTP port. Defaults to 1025.Locally 1025
SMTP_USERNoNoSMTP username. Auth is skipped when empty.Your SMTP provider
SMTP_PASSWORDNoNoSMTP password.Your SMTP provider
SMTP_SECURENoNotrue to force TLS. Otherwise TLS is used only on port 465.Your SMTP provider
RESEND_API_KEYWith resendNoResend API key. The app throws at send time if EMAIL_PROVIDER=resend and this is empty.Resend dashboard

See emails with Resend for the delivery setup.

Storage

The kit talks to any S3-compatible service through the AWS SDK. Locally that is MinIO from the Compose stack. apps/web/next.config.ts derives a next/image remotePatterns entry from S3_ENDPOINT, so uploaded images render from whatever endpoint you configure.

VariableRequiredPublicPurposeWhere to get it
S3_ENDPOINTYesNoS3 API endpoint. Also used for the image remotePatterns entry.Your provider; locally http://127.0.0.1:9000
S3_REGIONYesNoBucket region.Your provider; locally us-east-1
S3_ACCESS_KEY_IDYesNoAccess key.Your provider; locally minio
S3_SECRET_ACCESS_KEYYesNoSecret key.Your provider; locally minio123
S3_BUCKET_PUBLICYesNoBucket for public objects such as avatars.Create it; locally nextbase-public is created by minio-init
S3_BUCKET_PRIVATEYesNoBucket for private uploads served through presigned URLs.Create it; locally nextbase-private
S3_FORCE_PATH_STYLENoNotrue for path-style URLs (needed by MinIO).true locally; usually false for AWS S3

Realtime and cache

Realtime in the browser is optional in production. apps/web/src/lib/realtime/endpoint.ts ignores NEXT_PUBLIC_CENTRIFUGO_URL in production unless it is a wss:// URL that doesn't point at localhost.

VariableRequiredPublicPurposeWhere to get it
NEXT_PUBLIC_CENTRIFUGO_URLNoYesWebSocket URL the browser connects to.Your Centrifugo deployment; locally ws://127.0.0.1:8000/connection/websocket
CENTRIFUGO_URLNoNoServer-side copy of the WebSocket URL.Same as above
VALKEY_URLNoNoValkey or Redis URL for Better Auth's secondary storage, rate limits and locks. When unset, the kit uses the Postgres tables auth_secondary_storage and auth_rate_windows and advisory locks instead.Your Valkey host; locally redis://127.0.0.1:6379
CENTRIFUGO_API_URLWith realtimeNoCentrifugo HTTP API used by the outbox dispatcher to publish.Your Centrifugo deployment; locally http://127.0.0.1:8000/api
CENTRIFUGO_API_KEYWith realtimeNoAPI key for that HTTP API.Centrifugo config; the local one is in apps/database/local/centrifugo.json
CENTRIFUGO_TOKEN_HMAC_SECRETWith realtimeNoHMAC secret used to sign 15-minute connection tokens for the browser. At least 16 characters.Centrifugo config; must match the server

Billing (optional)

Billing uses the Better Auth Stripe plugin. Without these, billing pages render a disabled state.

VariableRequiredPublicPurposeWhere to get it
STRIPE_SECRET_KEYNoNoStripe secret key.Stripe dashboard
STRIPE_WEBHOOK_SECRETNoNoSigning secret for the webhook at /api/stripe/webhooks on the web app.Stripe webhook settings
STRIPE_PRO_PRICE_IDNoNoMonthly price for the pro plan.Stripe product catalog
STRIPE_PRO_ANNUAL_PRICE_IDNoNoAnnual price for pro.Stripe product catalog
STRIPE_ENTERPRISE_PRICE_IDNoNoMonthly price for the enterprise plan. The plan is registered only when this is set.Stripe product catalog
STRIPE_ENTERPRISE_ANNUAL_PRICE_IDNoNoAnnual price for enterprise.Stripe product catalog

AI (optional)

VariableRequiredPublicPurposeWhere to get it
OPENAI_API_KEYNoNoEnables the chat and image features.OpenAI dashboard
OPENAI_CHAT_MODELNoNoChat model name. Defaults to gpt-4o-mini.Your choice
OPENAI_IMAGE_MODELNoNoImage model name. Defaults to dall-e-2.Your choice

Analytics and monitoring (optional)

VariableRequiredPublicPurposeWhere to get it
NEXT_PUBLIC_POSTHOG_KEYNoYesPostHog project key.PostHog project settings
NEXT_PUBLIC_POSTHOG_HOSTNoYesPostHog host URL.PostHog project settings
NEXT_PUBLIC_GA_MEASUREMENT_IDNoYesGoogle Analytics measurement ID.Google Analytics
NEXT_PUBLIC_VERCEL_ANALYTICS_ENABLEDNoYestrue to load Vercel Analytics.Set true on Vercel if you want it
NEXT_PUBLIC_SENTRY_DSNNoYesSentry DSN. The Sentry wrapper in apps/web/next.config.ts is commented out; enable it there as well.Sentry project settings

Social login (optional)

The server registers a provider only when both of its variables are set. See authentication for what the login form shows.

VariableRequiredPublicPurposeWhere to get it
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRETNoNoGoogle OAuth app.Google Cloud console
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRETNoNoGitHub OAuth app.GitHub developer settings
TWITTER_CLIENT_ID, TWITTER_CLIENT_SECRETNoNoX (Twitter) OAuth app.X developer portal

Variables used by scripts, tests and internal routes

These are read by seeds, tests or single routes and are not in either example file.

VariablePurpose
REVALIDATE_SECRETDeclared in env.ts as optional. Shared secret for POST /api/revalidate on the web app (sent in the x-revalidate-secret header) and for POST /api/internal/admin-revalidate (sent as a Bearer token), which the admin app calls after mutations to refresh the public web caches. Set the same value on both apps; both routes fail closed when it is unset.
SEED_PASSWORDPassword for the fixture accounts created by pnpm --filter web db:seed. Defaults to password123.
SEED_ADMIN_EMAILEmail of the admin row created by the database seed. Defaults to admin@example.com.
NEXTBASE_SEED_MODESet to true by the clean-migration script so seeding skips verification emails, rate limits and secondary storage.
RUN_INTEGRATION_TESTSSet to true by pnpm test:integration to enable the integration suites.
MCP_TEST_PG_URLLoopback Postgres URL for apps/web/src/mcp/pg-store.integration.test.ts. Non-loopback hosts are rejected.
SITE_URL, NEXT_PUBLIC_SITE_URLSet by apps/web/.env.test and the Playwright web server to the test origin on port 3100; the admin app's template sets NEXT_PUBLIC_SITE_URL to the admin origin.
PORTPort for the Playwright web server. Defaults to 3100.
ADMIN_BASE_URLOrigin the Playwright config builds and starts the admin app on. Defaults to http://localhost:3101.

For production, see the list of what to set on each Vercel project in deployment.

Installation

Clone the Next.js + Neon starter, start the local Postgres stack with Docker, run the web and admin apps, seed and sign in.

Authentication

How Better Auth is configured in the Next.js + Neon starter, how sessions, the separate admin app and impersonation work, and what protects your data.

On this page

DatabaseAuth and app originIndependent app originsAdmin app (apps/admin/.env.local.example)EmailStorageRealtime and cacheBilling (optional)AI (optional)Analytics and monitoring (optional)Social login (optional)Variables used by scripts, tests and internal routes