Deploy to Vercel
Vercel builds your app, and npx convex deploy in the build command deploys your Convex functions in the same build. Production builds deploy to your production Convex deployment. Preview builds get a Convex preview deployment of their own, with a fresh, empty database.
1. Create a project
In the Vercel dashboard, click Add New…, then Project, and import your Git repository.

2. Set the build command
Keep the framework preset Vercel detects (SvelteKitNext.js) and override the build command:

npx convex deploy runs your framework’s build with the URLs of the Convex deployment it targets, then deploys your Convex functions there. It sets PUBLIC_CONVEX_URL and PUBLIC_CONVEX_SITE_URLNEXT_PUBLIC_CONVEX_URL and NEXT_PUBLIC_CONVEX_SITE_URL. Don’t add these in Vercel.
3. Add the deploy keys in Vercel
Generate a production deploy key and a preview deploy key in the Convex dashboard, and add each to its Vercel environment as CONVEX_DEPLOY_KEY:
That’s the only variable Vercel needs. Everything else lives in Convex.
4. Set the Convex environment variables
Convex functions read their variables from the deployment they run on, so set them in Convex in two places:
- Your production deployment’s settings, for production.
- Project Settings, Environment Variable Defaults, for every new development and preview deployment. Convex keeps a separate set of defaults for each deployment type. Each preview build creates a new deployment, which copies the preview defaults when it’s created.
Production:
Defaults for development deployments, with my-app as your Vercel project name and acme as your Vercel team slug:
Defaults for preview deployments, the same plus the protocol:
From the CLI, npx convex env default set NAME value --type preview sets a preview default and --type dev a development one.
Use a different BETTER_AUTH_SECRET for production than for development and previews. Without a secret in the defaults, a preview deployment has none.
Allowed hosts
BETTER_AUTH_ALLOWED_HOSTS is a comma-separated list of hosts, and * matches any characters, dots included. Vercel names preview URLs <project>-<hash>-<team>.vercel.app and <project>-git-<branch>-<team>.vercel.app, so my-app-*-acme.vercel.app matches the previews of one project. Don’t use *.vercel.app: it trusts every app on Vercel. For production, list your exact domains, and add *.acme.com only if every subdomain should be trusted. See Better Auth’s Dynamic Base URL for the pattern rules.
BETTER_AUTH_FALLBACK_URL is used only when Better Auth runs without an incoming request, as in Convex queries, mutations, triggers, scheduled functions and device authorization. All previews share the one value from the defaults, so links built without a request point there.
Without BETTER_AUTH_BASE_URL_PROTOCOL, cookies get the __Secure- prefix and the Secure attribute only when BETTER_AUTH_FALLBACK_URL is an https URL. Production, with https://acme.com, needs nothing more. Previews are served over HTTPS but share the http://localhost fallback URL, so the preview defaults set BETTER_AUTH_BASE_URL_PROTOCOL="https", or preview cookies go without Secure. Leave it out of the development defaults, because local development runs on http. Switching a deployment to Secure cookies renames them to __Secure-…, so people signed in before sign in once more.
Feature variables
Copy the variables of the features you turned on to both the production deployment and the defaults. The feature pages explain each one.
| Variable | Needed for |
|---|---|
RESEND_API_KEY, EMAIL_SEND_FROM | sendEmails: true. Every email fails without EMAIL_SEND_FROM. |
RESEND_WEBHOOK_SECRET | Resend delivery events. The webhook URL is https://<deployment>.convex.site/resend-webhook, so each deployment needs its own webhook in Resend. |
BRAND_LOGO_URL | A logo in the emails (optional) |
REOON_EMAIL_VERIFIER_TOKEN | validateEmails: true |
DEVICE_AUTHORIZATION_CLIENT_ID | deviceAuthorization: true |
<PROVIDER>_CLIENT_ID, <PROVIDER>_CLIENT_SECRET | Each social provider, for example GITHUB_CLIENT_ID. TikTok uses TIKTOK_CLIENT_KEY instead of a client id. |
ADMIN_USER_IDS | admin: true. Set it per deployment, not in the defaults. |
ADMIN_USER_IDS holds Better Auth user ids, and each preview deployment starts with an empty database, so an id from development or production matches nobody there. Sign up on the preview, then set the variable on that deployment in the Convex dashboard.
See Configure features for the flags and Sending emails for the Resend setup.
5. Deploy
Click Deploy. Vercel deploys again on every push: your production branch to production, other branches to previews.
How preview deployments work
When Vercel builds a preview:
npx convex deployreads the previewCONVEX_DEPLOY_KEY.- It creates or updates a Convex preview deployment named after the Git branch (
VERCEL_GIT_COMMIT_REF). A new deployment copies the environment variable defaults. - It runs your build with the Convex URLs of that preview deployment, then deploys your Convex functions to it.
- The preview site and its Better Auth requests use that preview deployment.