Leave Localhost logoLeave LocalhostDocs

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:check

release: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_NAME
  • CONVEX_SITE_URL
  • SITE_URL
  • BETTER_AUTH_SECRET
  • AUTH_GOOGLE_ID
  • AUTH_GOOGLE_SECRET
  • RESEND_API_KEY
  • RESEND_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/resend

Product 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 build

Start command:

bun --cwd apps/app start

Marketing 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 build

Start command:

bun --cwd apps/marketing start

Documentation 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 build

Start command:

bun --cwd apps/docs start

Vercel 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/microsoft

For local development, use:

http://localhost:3000/api/auth/callback/google
http://localhost:3000/api/auth/callback/microsoft

If you use preview domains, add them as trusted origins or provider callback URLs where required.

Launch Checks

  • Confirm bun run release:check passes with the intended production-like env.
  • Confirm APP_NAME is your product name, not the starter name.
  • Confirm packages/config/src/brand.ts has your static browser-visible product identity and that it matches backend APP_NAME where 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, and NEXT_PUBLIC_CONVEX_SITE_URL.
  • Confirm BETTER_AUTH_SECRET is a production secret, not a starter placeholder.
  • Confirm AUTH_TRUSTED_ORIGINS includes 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_EMAIL sender 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, and SENTRY_PROJECT, and that source-map upload is enabled for the deployment.
  • If billing is enabled, confirm BILLING_PROVIDER names 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_PROVIDER unset 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.

  1. Deploy the release candidate with production-like Sentry env values and a non-production Sentry environment label such as preview or staging.

  2. 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");
    });
  3. 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.

  4. Confirm both events appear in the intended Sentry project with readable stack traces, source-mapped frames, release metadata, and the expected environment label.

  5. 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.

On this page