Deployment
How to deploy the product app, marketing site, documentation site, and Convex backend to production.
This repo has four production surfaces:
apps/app: authenticated product app.apps/marketing: public marketing site.apps/docs: documentation site.packages/backend: Convex backend.
Deploy the three Next.js apps as separate projects. Deploy the backend through Convex.
Preflight
Before deploying, run the release gate from the repository root:
bun run release:checkrelease:check runs bun check-env --release, bun run lint,
bun run typecheck, bun run test, and bun run build in that order. Release
environment validation blocks required services, including Resend for the
enabled password and magic-link auth flows and Sentry for launch observability.
It does not fail when billing, analytics, Microsoft auth, or other optional
integrations are intentionally disabled.
Convex
Create or select a Convex project, then configure backend environment variables in the Convex dashboard or through the CLI:
bun --cwd packages/backend convex env set NAME "value"At minimum, production Convex needs the backend values from Environment Variables:
APP_NAMECONVEX_SITE_URLSITE_URLBETTER_AUTH_SECRETAUTH_GOOGLE_IDAUTH_GOOGLE_SECRETRESEND_API_KEYRESEND_AUTH_FROM_EMAIL
Add optional provider variables for Microsoft auth, billing, analytics, and
webhooks only when those integrations are enabled. RESEND_FROM_EMAIL is an
optional sender override for non-auth transactional email; auth email uses
RESEND_AUTH_FROM_EMAIL.
Webhook URLs use the Convex site domain. Register only the route for your
active billing provider — the one named by BILLING_PROVIDER. The other
routes are not registered:
https://your-convex-deployment.convex.site/webhooks/polar
https://your-convex-deployment.convex.site/webhooks/stripe
https://your-convex-deployment.convex.site/webhooks/lemon-squeezy
https://your-convex-deployment.convex.site/webhooks/resendProduct App
Deploy apps/app as its own Next.js project.
Production environment variables:
NEXT_PUBLIC_CONVEX_URL=
NEXT_PUBLIC_CONVEX_SITE_URL=
NEXT_PUBLIC_APP_URL=
# Optional: a docs link target; omit to hide the Documentation nav links.
NEXT_PUBLIC_DOCS_URL=
NEXT_PUBLIC_AUTH_MICROSOFT_ENABLED=
NEXT_PUBLIC_ANALYTICS_ENABLED=
NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN=
NEXT_PUBLIC_POSTHOG_HOST=
NEXT_PUBLIC_SENTRY_DSN=
SENTRY_AUTH_TOKEN=
SENTRY_ORG=
SENTRY_PROJECT=Only set optional values for integrations you use.
Build command:
bun --cwd apps/app buildStart command:
bun --cwd apps/app startMarketing Site
Deploy apps/marketing as a separate Next.js project. It does not use Convex.
Production environment variables:
NEXT_PUBLIC_SITE_URL=
NEXT_PUBLIC_APP_URL=
NEXT_PUBLIC_DOCS_URL=
NEXT_PUBLIC_PURCHASE_URL=
NEXT_PUBLIC_ANALYTICS_ENABLED=
NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN=
NEXT_PUBLIC_POSTHOG_HOST=Build command:
bun --cwd apps/marketing buildStart command:
bun --cwd apps/marketing startDocumentation Site
Deploy apps/docs as a separate Next.js project. It renders the repository
root docs/ directory with Fumadocs and has no Convex dependency.
Production environment variables:
NEXT_PUBLIC_DOCS_URL=
NEXT_PUBLIC_SITE_URL=Set NEXT_PUBLIC_DOCS_URL to the docs domain (for example
https://docs.leavelocalhost.com); it drives the canonical metadata base. Set
NEXT_PUBLIC_SITE_URL to the external marketing destination rendered in the
docs navbar.
Build command:
bun --cwd apps/docs buildStart command:
bun --cwd apps/docs startVercel Notes
The repo includes apps/app/vercel.json, apps/marketing/vercel.json, and
apps/docs/vercel.json.
For Vercel, create one project with root directory apps/app, another with
root directory apps/marketing, and a third with root directory apps/docs
(assign the docs domain to it). Configure each project's environment variables in
Vercel, then connect all three to the same repository.
The Convex deployment should be configured separately from Vercel. Follow Convex's Vercel deployment guidance for production deployment and preview deployments.
Auth Callback URLs
Set OAuth redirect URLs to the production app domain:
https://app.yourdomain.com/api/auth/callback/google
https://app.yourdomain.com/api/auth/callback/microsoftFor local development, use:
http://localhost:3000/api/auth/callback/google
http://localhost:3000/api/auth/callback/microsoftIf you use preview domains, add them as trusted origins or provider callback URLs where required.
Launch Checks
- Confirm
bun run release:checkpasses with the intended production-like env. - Confirm
APP_NAMEis your product name, not the starter name. - Confirm
packages/config/src/brand.tshas your static browser-visible product identity and that it matches backendAPP_NAMEwhere appropriate. - Confirm each deployable's local canonical and cross-app URLs are set:
NEXT_PUBLIC_APP_URL,NEXT_PUBLIC_SITE_URL,NEXT_PUBLIC_DOCS_URL,SITE_URL,CONVEX_SITE_URL,NEXT_PUBLIC_CONVEX_URL, andNEXT_PUBLIC_CONVEX_SITE_URL. - Confirm
BETTER_AUTH_SECRETis a production secret, not a starter placeholder. - Confirm
AUTH_TRUSTED_ORIGINSincludes every production or preview origin that should be accepted and excludes unapproved origins. - Confirm OAuth providers use production callback URLs.
- Confirm Resend sender domains are verified before sending production email,
especially the
RESEND_AUTH_FROM_EMAILsender used for verification, password reset, magic link, and invitation email. - Confirm Sentry is configured for the product app with
NEXT_PUBLIC_SENTRY_DSN,SENTRY_AUTH_TOKEN,SENTRY_ORG, andSENTRY_PROJECT, and that source-map upload is enabled for the deployment. - If billing is enabled, confirm
BILLING_PROVIDERnames the intended active provider, provider product/price IDs are production values, and the provider webhook points at the matching Convex site URL route. - If billing is disabled, leave
BILLING_PROVIDERunset and do not register billing webhooks. - Confirm Convex has all backend runtime variables, not just
packages/backend/.env. - Run through Delete Before Launch.
- Run
rg "Leave Localhost|leavelocalhost.com"and verify remaining matches are intentional docs or starter-owned references.
Sentry Proof
Use a staging or preview deployment as the default observability proof. Do not add a public production error route.
-
Deploy the release candidate with production-like Sentry env values and a non-production Sentry environment label such as
previeworstaging. -
Trigger one controlled browser error from the deployed product app. For example, open the browser console on the preview app and run:
setTimeout(() => { throw new Error("release-gate-client-error"); }); -
Trigger one controlled request/server error in the preview deployment without mutating production data. Prefer an existing staging-only failing request. If there is no safe existing path, create a temporary preview branch commit that throws from an authenticated server-rendered page under a private preview env flag, deploy it, request that page while signed in, then remove the temporary change before merging.
-
Confirm both events appear in the intended Sentry project with readable stack traces, source-mapped frames, release metadata, and the expected environment label.
-
Record the Sentry event links or issue IDs in the release PR or release notes.
Smoke Test Record
Before promoting the deployment, assign one release owner to record pass/fail results and notes for these scenarios in the release PR or release notes:
- Password signup, email verification, sign-in, and password reset.
- Magic link sign-in and each enabled OAuth provider.
- Invitation acceptance and organization/workspace switching.
- Dashboard loading, account deletion, and 2FA enrollment/sign-in.
- Billing checkout and webhook-driven entitlement updates only when billing is enabled.
Use production-like URLs and provider sandboxes for preview/staging. Use live provider configuration only when you are intentionally validating production.