Vercel Deployment
Link the template to the correct Vercel project, configure Production values safely, deploy, and run bounded production checks.
Deploy the single Next.js app to Vercel after the production Supabase project, Stripe test-mode objects, and required environment values are ready. Vercel CLI owns project linking, environment configuration, and deployment.
Use the intended Vercel account, team, and project. Never put database URLs, API keys, signing secrets, or tokens in command arguments, terminal logs, or screenshots.
Deploy to production
Prepare hosted Supabase
Create or select the project in Supabase, then authenticate and link the checked-out template:
bunx supabase login
bunx supabase link --project-ref <project-ref>
bunx supabase db pushPut the hosted DATABASE_URL and the other required app values in ignored
.env for local operator use. Check the app tables without calling the
Supabase management API:
./coremvp db verifyCompare the configured connection identity—endpoint, configured db, and
configured user—with the connection shown in that project's Connect
panel. For a Shared Pooler URL, configured user must contain
postgres.<project-ref>. Stop if any configured field differs. Continue
only when every expected app table is present. The verifier does not query
Supabase project metadata.
Check the Vercel identity and project
Authenticate, inspect the current identity, and list projects in the intended account or team before linking:
bunx vercel login
bunx vercel whoami
bunx vercel teams lsFor a personal-account project, omit --scope. Vercel rejects a personal
username passed as a scope:
bunx vercel project ls
bunx vercel link --project <project-name>For a team project, use the team slug shown by teams ls:
bunx vercel project ls --scope <team-slug>
bunx vercel link --project <project-name> --scope <team-slug>If the identity, team, or project is wrong, stop before linking. Vercel writes ignored local link metadata. The provider CLI uses that link to select the project for later commands.
Add the Production environment
Add public values with Vercel's interactive prompt:
bunx vercel env add NEXT_PUBLIC_APP_URL production
bunx vercel env add NEXT_PUBLIC_SUPABASE_URL production
bunx vercel env add NEXT_PUBLIC_SUPABASE_ANON_KEY production
bunx vercel env add NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY productionAdd server values the same way. Mark secrets sensitive:
bunx vercel env add DATABASE_URL production --sensitive
bunx vercel env add SUPABASE_SERVICE_ROLE_KEY production --sensitive
bunx vercel env add STRIPE_SECRET_KEY production --sensitive
bunx vercel env add STRIPE_WEBHOOK_SECRET production --sensitive
bunx vercel env add RESEND_API_KEY production --sensitive
bunx vercel env add TEST_EMAIL_CAPTURE_TOKEN production --sensitive
bunx vercel env add EMAIL_DELIVERY_MODE production
bunx vercel env add RESEND_FROM_EMAIL productionThe CLI prompts for each value so secrets do not appear in argv. If a key already exists, update it without deleting it. Keep public updates readable, and mark every server-secret update sensitive:
bunx vercel env update NEXT_PUBLIC_APP_URL production
bunx vercel env update DATABASE_URL production --sensitive
bunx vercel env ls productionRepeat the first form for existing public keys and the --sensitive form
for existing server secrets.
Create TEST_EMAIL_CAPTURE_TOKEN in a password manager before you paste it
into the sensitive Vercel prompt, and retain that same value for ignored
local .env. Vercel sensitive values cannot be read back after creation.
If the retained token is lost, update it from a new password-manager value,
deploy again, and replace the local copy before lifetime proof.
See Environment Variables for which values are public, server-only, proof-only, or required at launch.
Configure provider callbacks
In Supabase Dashboard → Authentication → URL Configuration, set the Site URL to the hosted app URL and add its redirect pattern.
In Stripe Dashboard test mode, create one webhook endpoint at
https://<project-name>.vercel.app/api/webhooks/stripe. Select these exact
events:
product.createdproduct.updatedproduct.deletedprice.createdprice.updatedprice.deletedcheckout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deleted
Update STRIPE_WEBHOOK_SECRET in Vercel Production with that endpoint's
signing secret.
Deploy after Production values are complete
bunx vercel deploy --prodEnvironment changes apply to subsequent deployments. Deploy again after adding or updating a Production value.
Check liveness, Auth, and billing
Run the bounded route smoke first:
./coremvp prod e2e smoke https://<project-name>.vercel.appThis checks only /, /api/health, and /pricing. It does not prove Auth,
billing, callbacks, or webhook persistence.
The production proof CLI reads local values separately from Vercel. In a fresh checkout, restore readable Production values first:
bunx vercel env pull .env --environment=production
chmod 600 .envSensitive Vercel values cannot be read back. Treat every [SENSITIVE]
value as missing. In the next steps, replace the existing
SUPABASE_SERVICE_ROLE_KEY and TEST_EMAIL_CAPTURE_TOKEN assignments with
the real local values.
Keep exactly one assignment for each key; do not append duplicate lines
because the template CLI reads the first assignment.
In Supabase Dashboard, open
Project Settings → API Keys → Legacy API Keys for the project ref in
NEXT_PUBLIC_SUPABASE_URL, then paste service_role into ignored .env as
SUPABASE_SERVICE_ROLE_KEY. The authenticated CLI is also read-only, but
keep its key response out of terminal output:
mkdir -p tmp/provider
umask 077
bunx supabase projects api-keys --project-ref <project-ref> --output-format json > tmp/provider/supabase-api-keys.jsonOpen the ignored file in a private editor, copy the entry named
service_role into .env, then delete the temporary file. Stop if the key
is missing, redacted, or belongs to a different project.
rm tmp/provider/supabase-api-keys.jsonRetrieve TEST_EMAIL_CAPTURE_TOKEN from the password-manager item retained
when the sensitive Vercel value was created and paste it into ignored
.env. If it is unavailable, stop and rotate it through the Vercel prompt,
deploy again, and save the replacement. Verify the deployed capture mode
without sending or printing the token:
curl -sS -w '\nHTTP %{http_code}\n' https://<project-name>.vercel.app/api/testing/email-captures/onboardingExpected result: the app response
{"message":"Unauthorized","ok":false} followed by HTTP 401. Require
both markers because a provider or deployment-protection 401 does not
prove that the app route ran. An app response with HTTP 404 means
test_capture is disabled; correct the Vercel Production values and redeploy
before lifetime proof. The subsequent lifetime proof verifies that the
retained local token matches the deployment. Stop if the local Supabase
project or capture token does not match the deployed app.
Run the matching production flows against the same exact URL:
export TEST_BASE_URL=https://<project-name>.vercel.app
export PROD_E2E_AUTH_MODE=signup
./coremvp prod e2e auth
unset PROD_E2E_AUTH_MODE
./coremvp prod e2e billing:lifetime
export PROD_E2E_AUTH_MODE=signup
./coremvp prod e2e billing:subscriptionCustom domain and live billing
Add the domain in Vercel, follow the DNS records Vercel provides, then update
the Supabase Site URL and Redirect URLs. Update NEXT_PUBLIC_APP_URL in Vercel
Production and deploy again before running the smoke, Auth, and billing checks
against the custom domain.
Only after the test-mode path passes should you create the live Stripe objects
and live webhook endpoint. Replace every Stripe test value with its matching
live value, set real email delivery values, remove proof-only values such as
TEST_EMAIL_CAPTURE_TOKEN, deploy again, and send a real transactional email
before launch.
Preview deployments
Preview is not part of the beginner production path. Do not point an arbitrary feature-branch preview at production Supabase, Stripe, or webhook secrets. Use isolated provider values before configuring Preview environment variables.
Was this page helpful?