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, selected billing provider, and required environment values are ready. Use Stripe test mode or Polar sandbox for the first billing checks. 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 from the
selected Supabase project in ignored .env for local operator use.
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
Open Supabase Settings → API Keys
and select Publishable and secret API keys. If an older project shows
Create new API keys, create the default key pair; the existing legacy
keys keep working while you migrate. Continue when both current keys are
visible. Use the publishable key for
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY and the server secret for
SUPABASE_SECRET_KEY.
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_PUBLISHABLE_KEY productionFor Polar, follow hosted Polar setup to save the server-only provider selection, sandbox offers, access token and hosted webhook secret. A Polar-only app does not need Stripe settings. Keep existing Stripe settings for retained billing and outstanding deliveries.
For Stripe, add its public settings:
bunx vercel env add NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY production
bunx vercel env add NEXT_PUBLIC_STRIPE_MODE productionUse test for NEXT_PUBLIC_STRIPE_MODE during setup. Add the test account's
Price inputs only for the offers you sell from the
shared environment example.
Keep displayed amounts in src/config/pricing.config.ts aligned with Stripe.
These public inputs are embedded during the next build.
Add server values the same way. Mark secrets sensitive:
bunx vercel env add DATABASE_URL production --sensitive
bunx vercel env add SUPABASE_SECRET_KEY production --sensitive
bunx vercel env add RESEND_API_KEY production --sensitive
bunx vercel env add EMAIL_DELIVERY_MODE production
bunx vercel env add RESEND_FROM_EMAIL productionUse real application email for the manual hosted Polar journey. For Stripe
billing, also add its server secrets. Its automated guest checks use
EMAIL_DELIVERY_MODE=test_capture and a capture token instead of real delivery:
bunx vercel env add STRIPE_SECRET_KEY production --sensitive
bunx vercel env add STRIPE_WEBHOOK_SECRET production --sensitive
bunx vercel env add TEST_EMAIL_CAPTURE_TOKEN production --sensitiveThe 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.
For Stripe's automated guest checks, 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 Hosted 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.
For Polar, follow Create a hosted destination for the selected sandbox organization and this app's exact origin. Subscribe to the documented order and subscription events and retain that destination's signing secret; the local listener secret does not apply to hosted delivery.
For Stripe, in 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.completedinvoice.paidcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deleted
Update STRIPE_WEBHOOK_SECRET in Vercel Production with that Stripe 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_SECRET_KEY and TEST_EMAIL_CAPTURE_TOKEN assignments with
the real local values when the selected checks need them.
Keep exactly one assignment for each key; do not append duplicate lines
because the template CLI reads the first assignment.
In Supabase Dashboard, open Settings → API Keys → Publishable and secret
API keys for the project ref in NEXT_PUBLIC_SUPABASE_URL. Copy the
server secret into the existing SUPABASE_SECRET_KEY assignment in ignored
.env. Stop if the key is missing or belongs to a different project.
The capture steps below are for automated Stripe guest proof. Polar uses the manual hosted sandbox checks with real application email instead.
For Stripe guest proof, retrieve 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 Auth against the same exact URL:
export TEST_BASE_URL=https://<project-name>.vercel.app
export PROD_E2E_AUTH_MODE=signup
./coremvp prod e2e authThe hosted billing lanes currently exercise Stripe test mode only. For Polar, use the manual hosted checks linked above. For Stripe, continue:
export TEST_BASE_URL=https://<project-name>.vercel.app
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 selected provider's hosted test path passes should you prepare
live billing. For Polar, follow Launch Your App
for production credentials, offers and a separate hosted webhook destination;
set POLAR_ENVIRONMENT=production. Sandbox grants then stop supplying access
and management while their history remains stored.
For Stripe, create the live Stripe objects
and live webhook endpoint. Set NEXT_PUBLIC_STRIPE_MODE=live and replace the
sold offers' catalog inputs and Stripe credentials with matching live values.
Unmapped offers remain unavailable in live mode; use a matching live secret key. 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, billing credentials, or webhook secrets. Use Hosted Environment Variables for the shared input block and the values to change for Preview.
Was this page helpful?