Deploy to Vercel
Step-by-step production deployment of the web app on Vercel — no code changes required
The recommended path. Vercel runs Next.js 16 natively, understands the pnpm + Turborepo monorepo, and needs no code changes. Budget ~30 minutes plus DNS propagation.
Complete the shared prerequisites first (Neon, Stripe, Resend, OAuth apps).
1. Import the project
- Push the repository to GitHub and import it at vercel.com/new
- Set Root Directory to
apps/web - Leave the framework preset on Next.js.
apps/web/vercel.jsonalready sets a custom Build Command —cd ../.. && pnpm turbo build --filter=app— so Vercel doesn't fall back to a barenext build, which would skip Prisma client generation. The generated client is gitignored, so without the Turbo build the deploy fails; this isn't optional and needs no manual setup
2. Environment variables
Add these in Project → Settings → Environment Variables (Production). This mirrors apps/web/.env.example:
# App
NEXT_PUBLIC_APP_URL=https://yourdomain.com
NEXT_PUBLIC_APP_NAME=YourApp
NEXT_PUBLIC_DOCS_URL=https://docs.yourdomain.com
# Database (Neon pooled connection string)
DATABASE_URL=postgresql://...@ep-*-pooler.*.neon.tech/...
# Better Auth
BETTER_AUTH_SECRET= # openssl rand -base64 48
BETTER_AUTH_URL=https://yourdomain.com
BETTER_AUTH_TRUSTED_ORIGINS=https://yourdomain.com
# OAuth (optional)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
# Stripe
STRIPE_SECRET_KEY=sk_live_... # or sk_test_ while testing
STRIPE_WEBHOOK_SECRET=whsec_... # added after step 5
STRIPE_PRO_PRICE_ID_MONTHLY=price_...
STRIPE_PRO_PRICE_ID_ANNUAL=price_...
STRIPE_ENTERPRISE_PRICE_ID_MONTHLY=price_...
STRIPE_ENTERPRISE_PRICE_ID_ANNUAL=price_...
# Email — with RESEND_API_KEY set, production auto-selects the Resend provider
EMAIL_FROM=noreply@yourdomain.com
RESEND_API_KEY=re_...
# Storage — serverless filesystems are ephemeral; use S3 in production
STORAGE_PROVIDER=s3
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=us-east-1
AWS_S3_BUCKET=your-bucket
AWS_S3_PUBLIC_URL=https://your-bucket.s3.amazonaws.com
STORAGE_PROVIDER=localwrites to./public/uploads, which does not persist on Vercel's serverless filesystem. Uses3for any real deployment.
Vercel's Hobby plan caps request bodies at 4.5 MB. The kit's upload limit (
MAX_FILE_SIZE_MBinpackages/utils/storage.ts) is set to 4 MB for this reason.
3. Push the database schema
From your machine, put the unpooled Neon URL in DIRECT_URL in packages/database/.env. Leave Vercel DATABASE_URL as the -pooler string. prisma.config.ts uses DIRECT_URL || DATABASE_URL; the pooler cannot run DDL.
cd packages/database && npx prisma db push4. Deploy and attach the domain
Trigger the first deploy, then add your domain under Settings → Domains. If the domain differs from what you set in step 2, update NEXT_PUBLIC_APP_URL, BETTER_AUTH_URL, and BETTER_AUTH_TRUSTED_ORIGINS and redeploy — NEXT_PUBLIC_* values are baked at build time.
5. Stripe webhook and OAuth callbacks
Follow the shared post-deploy configuration: register https://yourdomain.com/api/auth/stripe/webhook, copy the signing secret into STRIPE_WEBHOOK_SECRET, redeploy, and add the OAuth callback URLs.
6. Verify
Run the full verification checklist.
Vercel-specific notes
- Middleware (
apps/web/middleware.ts) runs on Vercel's edge runtime. It performs an optimisticgetSessionCookie()check with no database access — a missing cookie redirects to sign-in here. It cannot distinguish a valid session from a stale or garbage cookie (any present token string is truthy). Real enforcement happens in the layouts viarequireAuth. - Preview deployments get
*.vercel.appURLs that won't matchBETTER_AUTH_TRUSTED_ORIGINS; auth flows on previews will be limited unless you add the preview origin. - Rollback:
vercel rollbackrestores the previous deployment; the schema is managed separately via Prisma, so roll it back deliberately if a deploy included schema changes.