Setup
Turn on the optional integrations one at a time. Same shape per section so you never get lost.
Last updated on
7 min readQuickstart gets the app running with three env vars. This page is where you turn on the optional integrations: OAuth providers, captcha, billing, email, storage, AI, analytics, monitoring, and abuse protection. Every card follows the same shape: a short intro, the steps to do on the provider's side, the env vars to set, and a verification step. Anything you skip stays gracefully degraded in the UI; the kit always boots.
The Setup Doctor
Run pnpm setup:doctor after editing apps/web/.env. It validates the core vars, then prints one line per optional integration showing whether it's enabled, what's missing, and how to fix it.
Core setup
- DATABASE_URLOK
- NEXT_PUBLIC_APP_URLOK
- BETTER_AUTH_SECRETOK
Optional integrations
- Email (log)OK
- GitHub OAuthdisabledMissing GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
- Google OAuthOK
- Turnstile captchaOK
- Stripe billingOK
- PostHog analyticsOK
- Storage uploadsOK
- Contact formOK
- Abuse protectionOK
Use the doctor as your green-light check after each card below: the line you care about should read OK. See Commands And Scripts for what else the script does.
Optional Integrations
Email + password authentication ships enabled by default. Two-factor auth (TOTP + backup codes) and passkeys (WebAuthn) are wired and ready without any env vars; users opt in from /dashboard/account/security. Expand any integration below to turn it on.
GitHub OAuthOptionalLet users sign in with their GitHub account. Email + password keeps working alongside it.doctor:GitHub OAuth: OK
Create the OAuth app
- Open github.com/settings/developers -> OAuth Apps -> New OAuth App.
- Homepage URL: your
NEXT_PUBLIC_APP_URL(e.g.http://localhost:3000). - Authorization callback URL:
<NEXT_PUBLIC_APP_URL>/api/auth/callback/github. - Click Register application, then Generate a new client secret. Copy both the Client ID and Client Secret.
Set the env vars
| Var | Example |
|---|---|
GITHUB_CLIENT_ID | Iv1.abc123... |
GITHUB_CLIENT_SECRET | ghp_xyz... |
Verify
pnpm setup:doctor reports GitHub OAuth: OK. Visit /auth/sign-in and the GitHub button is active (not greyed out).
For deeper coverage (callback wiring, scopes, adding a new provider), see Authentication.
Google OAuthOptionalLet users sign in with their Google account. Same shape as GitHub; the provider console is different.doctor:Google OAuth: OK
Create the OAuth client
- Open console.cloud.google.com/apis/credentials -> Create Credentials -> OAuth client ID.
- Application type: Web application.
- Authorized JavaScript origins: your
NEXT_PUBLIC_APP_URL. - Authorized redirect URIs:
<NEXT_PUBLIC_APP_URL>/api/auth/callback/google. - Click Create. Copy the Client ID and Client Secret.
Set the env vars
| Var | Example |
|---|---|
GOOGLE_CLIENT_ID | 1234567890-abc.apps.googleusercontent.com |
GOOGLE_CLIENT_SECRET | GOCSPX-... |
Verify
pnpm setup:doctor reports Google OAuth: OK. The Google button is active on /auth/sign-in.
For deeper coverage, see Authentication.
Cloudflare TurnstileOptionalCaptcha protection on sign-up, sign-in, password reset, and the public contact form. Free for any traffic volume.doctor:Turnstile captcha: OK
Create the site widget
- Open dash.cloudflare.com -> Turnstile -> Add site.
- Widget mode: Managed (recommended).
- Domains: your production hostname plus
localhostfor local dev. - Copy the Site Key (public) and Secret Key (server-only).
Set the env vars
| Var | Example |
|---|---|
NEXT_PUBLIC_TURNSTILE_SITE_KEY | 0x4AAAAAAA... |
TURNSTILE_SECRET_KEY | 0x4AAAAAAA... |
Verify
pnpm setup:doctor reports Turnstile captcha: OK. Open /auth/sign-up and the captcha widget renders below the form.
For deeper coverage, see Security: Authentication, Sessions, And Captcha.
Billing (Stripe)OptionalSubscriptions, the customer portal, the dashboard pricing grid, and lifecycle emails. Stripe-hosted checkout; B2B / org-scoped.doctor:Stripe billing: OK
Create the Stripe products
- Sign up at stripe.com; stay in test mode for development.
- Create a Product called "Pro". Add two recurring Prices: monthly and yearly. Copy both price ids.
- In Developers -> API keys, copy your Secret key (
sk_test_*).
Register the webhook endpoint
In the Stripe Dashboard -> Developers -> Webhooks -> Add endpoint:
- URL:
<NEXT_PUBLIC_APP_URL>/api/webhooks/stripe - Events:
checkout.session.completed,customer.subscription.created,customer.subscription.updated,customer.subscription.deleted,customer.subscription.trial_will_end,invoice.finalized,invoice.payment_action_required,invoice.payment_failed,invoice.payment_succeeded.
Copy the signing secret (whsec_*).
For local dev, install the Stripe CLI and run stripe listen --forward-to localhost:3000/api/webhooks/stripe. The CLI prints a whsec_* secret to use locally.
Set the env vars
| Var | Example |
|---|---|
STRIPE_SECRET_KEY | sk_test_51M... |
STRIPE_WEBHOOK_SECRET | whsec_... |
STRIPE_PRICE_ID_PRO_MONTHLY | price_1MxAbc... |
STRIPE_PRICE_ID_PRO_YEARLY | price_1MxDef... |
Verify
pnpm setup:doctor reports Stripe billing: OK. Visit /dashboard/billing; the plan grid renders with the Pro card and a working "Upgrade" button.
For deeper coverage (subscription phases, customizing plans, the webhook story), see Billing.
Email (Plunk)OptionalTransactional email. The default log mode needs zero setup and writes to .local/email-outbox/. Switch to Plunk for production.doctor:Email (plunk): OK
EMAIL_DELIVERY_MODE auto-resolves: plunk if PLUNK_API_KEY is set, noop if NODE_ENV=test, otherwise log. You usually don't need to set it explicitly.
Local development (default)
No setup needed. The kit writes every outgoing email as an .eml file to .local/email-outbox/. Open the file in your mail client to inspect it.
Production (Plunk)
- Sign up at useplunk.com.
- Add and verify your sending domain. Plunk walks you through the SPF, DKIM, and DMARC DNS records.
- Create an API key from Settings -> API.
Set the env vars (production)
| Var | Example |
|---|---|
PLUNK_API_KEY | sk_pk_... |
EMAIL_DELIVERY_MODE | plunk (auto-resolves; only set explicitly to override) |
CONTACT_FORM_TO_EMAIL | hello@your-domain.com (only if the public contact form is enabled) |
Verify
pnpm setup:doctor reports Email (plunk): OK. Trigger a transactional email (sign up a new user; the verification email goes through Plunk's outbound API). The contact form additionally requires CONTACT_FORM_TO_EMAIL plus Turnstile.
For deeper coverage (delivery modes, React Email templates, swapping providers), see Email.
StorageOptionalS3-compatible object storage for avatars, organization logos, and chat image attachments. Pick a provider; the env-var skeleton is the same.doctor:Storage uploads: OK
Native AWS: no custom endpoint, region required. Path-style stays off (default).
AWS_REGION="us-east-1"
AWS_ENDPOINT_URL_S3=""
AWS_S3_FORCE_PATH_STYLE="false"
AWS_ACCESS_KEY_ID="<your-access-key-id>"
AWS_SECRET_ACCESS_KEY="<your-secret-access-key>"
NEXT_PUBLIC_S3_BUCKET_NAME_IMAGES="your-bucket-name"
NEXT_PUBLIC_S3_PUBLIC_URL="https://your-bucket.s3.us-east-1.amazonaws.com"R2 uses an account-scoped endpoint and the magic region auto. Free egress is the usual reason buyers pick it.
AWS_REGION="auto"
AWS_ENDPOINT_URL_S3="https://<account-id>.r2.cloudflarestorage.com"
AWS_S3_FORCE_PATH_STYLE="false"
AWS_ACCESS_KEY_ID="<your-r2-access-key-id>"
AWS_SECRET_ACCESS_KEY="<your-r2-secret-access-key>"
NEXT_PUBLIC_S3_BUCKET_NAME_IMAGES="your-bucket-name"
NEXT_PUBLIC_S3_PUBLIC_URL="https://pub-<bucket-id>.r2.dev"MinIO and most other self-hosted S3 servers expect path-style URLs. Endpoint is your MinIO host.
AWS_REGION="auto"
AWS_ENDPOINT_URL_S3="https://minio.your-domain.com"
AWS_S3_FORCE_PATH_STYLE="true"
AWS_ACCESS_KEY_ID="<your-minio-access-key>"
AWS_SECRET_ACCESS_KEY="<your-minio-secret-key>"
NEXT_PUBLIC_S3_BUCKET_NAME_IMAGES="your-bucket-name"
NEXT_PUBLIC_S3_PUBLIC_URL="https://minio.your-domain.com/your-bucket-name"Don't forget the bucket-side CORS rule (allow PUT from your app origin) and a lifecycle rule expiring tmp/ after 1 day. Without CORS, every upload fails silently. Both rules are copy-pasteable in Storage required bucket configuration.
Verify. pnpm setup:doctor reports Storage uploads: OK. Visit /dashboard/account and upload an avatar; it persists and shows up immediately on the navbar.
For deeper coverage (the presign + finalize pipeline, image validation, what gets stored where), see Storage.
AI (Vercel AI Gateway)OptionalOne key unlocks every supported provider through gateway(modelId). No per-provider keys, no swapping SDKs when a new model lands.
AI_GATEWAY_API_KEY is not yet in apps/web/.env.example. Add it to your .env manually. pnpm setup:doctor doesn't currently track the AI Gateway either; verify by sending a chat message instead.
Create a gateway key
- Sign in at vercel.com.
- Open the AI Gateway page from your dashboard.
- Create a new API key. Copy the value.
Set the env var
| Var | Example |
|---|---|
AI_GATEWAY_API_KEY | vck_... |
Verify
Visit /dashboard/ai-chat, send a message, and confirm tokens stream back. If the request fails at the SDK boundary with an authentication error, the key isn't being read; restart the dev server after editing .env.
For deeper coverage (the streaming chat handler, the ai-elements building blocks, billing gates per model), see AI.
Analytics (PostHog)OptionalProduct analytics, web analytics, session replay, and the foundation that Monitoring builds on.doctor:PostHog analytics: OK
Create a PostHog project
- Sign up at posthog.com.
- Create a project; copy the Project API Key from Project Settings.
- Note your API host (e.g.
https://us.i.posthog.com) and UI host (e.g.https://us.posthog.com). The exact values depend on which region you picked at signup; PostHog shows them on the same settings page.
Set the env vars
| Var | Example |
|---|---|
NEXT_PUBLIC_POSTHOG_KEY | phc_... |
NEXT_PUBLIC_POSTHOG_HOST | https://us.i.posthog.com |
NEXT_PUBLIC_POSTHOG_UI_HOST | https://us.posthog.com |
POSTHOG_PROXY_INGEST_HOST | optional; same as NEXT_PUBLIC_POSTHOG_HOST to enable the same-origin reverse proxy |
POSTHOG_PROXY_ASSET_HOST | optional; pair with POSTHOG_PROXY_INGEST_HOST |
Verify
pnpm setup:doctor reports PostHog analytics: OK. Trigger an action in the app; watch it appear in PostHog -> Live Events within seconds.
For deeper coverage (typed events, identification, session replay, the reverse-proxy story), see Analytics.
Monitoring (PostHog Source Maps and Logs)OptionalBuilds on Analytics. Adds de-minified stack traces in PostHog's Errors view and OpenTelemetry-shaped server logs.
Prerequisite: Analytics enabled. The two env vars below are read at build time only, so changing them requires a fresh build on every host. Set them in your build environment (Vercel, Fly, Render, Docker), not just .env.
Create a personal API key
- In PostHog -> Settings -> Personal API Keys -> Create personal API key.
- Scope: Project: write. Copy the key (
phx_...). - Note your numeric Project ID from Project Settings (e.g.
12345).
Set the env vars
| Var | Example |
|---|---|
POSTHOG_API_KEY | phx_... |
POSTHOG_PROJECT_ID | 12345 |
Verify
Run pnpm build. The build log includes a "Source maps uploaded" line from withPostHogConfig. Trigger an unhandled error in the app; PostHog -> Errors shows the issue with a de-minified stack trace.
For deeper coverage (the three pipelines, structured logging via OTLP, the reverse proxy), see Monitoring.
Abuse Protection (Upstash Redis)Required for productionSliding-window rate limits on auth flows, the contact form, AI chat, and uploads. Required before going to production.doctor:Abuse protection: OK
Create an Upstash Redis database
- Sign up at upstash.com.
- Create a Redis database. Any region is fine; the kit uses the REST API so latency is bounded by HTTPS, not Redis protocol.
- Open the database -> REST API panel. Copy the REST URL and REST Token.
Set the env vars
| Var | Example |
|---|---|
UPSTASH_REDIS_REST_URL | https://us1-active-xxx.upstash.io |
UPSTASH_REDIS_REST_TOKEN | AX... |
Verify
pnpm setup:doctor reports Abuse protection: OK. Send 10 sign-in requests in a row from the same IP; the eleventh returns Too Many Requests.
For deeper coverage (per-surface limits, fail-open vs fail-closed policy, Redis tuning), see Security: Abuse Protection (Upstash).
Where To Go Next
Environment Variables
Complete reference of every env var the kit reads, grouped by subsystem.
Troubleshooting
Fixes for friction points that come up most often during a first run.
Going To Production
Pre-launch checklist: branding, marketing, legal, billing, email, i18n, SEO, and env vars.
