Deployment
Provision a Supabase project, apply migrations and deploy the Next.js + Supabase starter's web and admin apps to Vercel.
The live demo runs on Vercel as two projects, the web app at nextjs-supabase.usenextbase.com and the admin app at nextjs-supabase-admin.usenextbase.com, sharing one hosted Supabase project. This page covers what is specific to this kit. For the platform-generic steps read deploy on Vercel. The repository also ships apps/web/netlify.toml (command = "pnpm run build") if you prefer Netlify for the web app; the env work is the same. The repository's own notes are in docs/admin-app.md.
Create and link the Supabase project
Create a project in the Supabase dashboard. From the database package, link the CLI to it:
cd apps/database
pnpm exec supabase link --project-ref <your-project-ref>The example env files have a commented SUPABASE_PROJECT_REF line you can use to remember the ref; neither app reads it.
Apply migrations
cd apps/database
pnpm exec supabase db pushThis applies everything in apps/database/supabase/migrations/, including 20261014114351_user_admin_mcp.sql, which creates mcp_records and the mcp_live_actor and mcp_projects functions. The MCP routes on both apps return errors until that migration is applied, so push before the first deploy.
seed.sql is for local development only; db push does not run it. Then regenerate types from the linked project if you want them to match production:
pnpm gen-typesConfigure Supabase Auth for both domains
In the project's Auth settings:
- Set the site URL to the web app's production origin, for example
https://app.example.com. Keep it on the web origin, not the admin origin. - Add redirect URLs for
/auth/callbackand/auth/confirmon both origins:https://app.example.com/auth/callback,https://app.example.com/auth/confirm,https://admin.example.com/auth/callbackandhttps://admin.example.com/auth/confirm. This mirrors the four entries in the localconfig.toml. - Keep email confirmations on if you want the same behaviour as local.
- Configure SMTP (the demo uses Resend) so verification, magic link and recovery emails are delivered. See emails.
- Add social provider credentials only for the providers you render in the web app. See authentication.
Preview deployments need their own redirect URLs if you test login there.
Create two Vercel projects
Import the repository twice. Set the root directory to apps/web for the first project and apps/admin for the second, and keep access to the whole workspace enabled so each build can resolve database and @nextbase/typescript-config. Vercel detects Next.js and pnpm from the lockfile; each project builds with its package's build script. Map your product domain to the web project and the admin domain to the admin project (the demo uses the <subdomain>-admin pattern).
The web build script first runs scripts/generate-env-local.ts. When NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY and SUPABASE_SECRET_KEY are present in the build environment, the script exits immediately. If any is missing, it tries to run supabase status and the build fails. Set all three before the first build. The admin build script is plain next build.
Set production environment variables
Use the exact names from environment variables. The minimum for the web project:
| Variable | Value |
|---|---|
NEXT_PUBLIC_SITE_URL | The web origin, no trailing path, for example https://app.example.com |
NEXT_PUBLIC_SUPABASE_URL | The hosted project's API URL |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY | The hosted project's publishable key |
SUPABASE_SECRET_KEY | The hosted project's secret key, server-only |
ADMIN_EMAIL | A sender address verified in Resend |
RESEND_API_KEY | Your Resend key |
REVALIDATE_SECRET | A random string, identical on both projects |
The minimum for the admin project:
| Variable | Value |
|---|---|
NEXT_PUBLIC_SITE_URL | The admin origin, for example https://admin.example.com |
NEXT_PUBLIC_WEB_APP_URL | The web origin, for example https://app.example.com. Support sign-in links and cache invalidation calls go here |
NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY, SUPABASE_SECRET_KEY | The same hosted project |
REVALIDATE_SECRET | The same value as the web project |
Leave USE_LOCAL_EMAIL unset on both. Add STRIPE_SECRET_KEY, NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY and STRIPE_WEBHOOK_SECRET to the web project if you keep billing, then create a Stripe webhook endpoint pointing at https://app.example.com/api/stripe/webhooks; add them to the admin project too if you use the payment gateway pages. Add ADMIN_EMAIL and RESEND_API_KEY to the admin project if admin actions send email. Add Sentry, PostHog, Unkey, OpenAI and OTEL variables only for the features you use. Never give a server secret a NEXT_PUBLIC_ prefix.
NEXT_PUBLIC_SITE_URL matters more here than in most kits: on each app it is the OAuth issuer for that app's MCP server ({NEXT_PUBLIC_SITE_URL}/oauth) and the resource prefix for /mcp/user or /mcp/admin. Set it to the exact origin users will visit; a mismatch breaks agent connections even when the site itself works.
Verify locally, then deploy
pnpm verify:localDeploy previews of both projects first, then production. If you use the repository's signoff flow, pnpm signoff runs the same checks and posts the commit status.
Make yourself an application admin
Find your user id in the Supabase dashboard after you sign up on the web app, then in the SQL editor:
select make_user_app_admin('<your-user-id>');Sign out and back in. Sign in at the admin origin's /login with the same email and password; /app-admin and /mcp/connections/admin are now available there. The function refuses to run from the app's own database roles, so this is the only supported path.
Post-deploy smoke checks
GET /api/healthon the web origin returns{"ok":true}.- Open
/sign-upon the web app, create an account, receive the verification email and finish onboarding with a new workspace. - Open
/login, sign in with the password, then try/forgot-passwordand finish at/update-password. - Create a project, invite a second user and accept the invitation from the email.
- Open
/mcp/connections/useron the web app and confirm the displayed server address uses the web origin. Open/.well-known/oauth-protected-resource/mcp/userand/.well-known/oauth-authorization-server/oauththere and check the URLs inside. - Follow connect an agent, approve a workspace and ask the agent to run
projects_list. - As an admin, open the admin origin:
/login, then/app-adminand/mcp/connections/admin. Open/.well-known/oauth-protected-resource/mcp/adminthere, then connect and calladmin_workspaces_list. The web origin's/mcp/adminshould return 404. - From
/app-admin/users, open a user and generate a support sign-in link; it must point at the web origin's/auth/confirmand sign you in there without touching the admin session. - Publish a blog post or changelog entry from the admin app and confirm it appears on the web app; a failure here usually means
REVALIDATE_SECRETorNEXT_PUBLIC_WEB_APP_URLdiffers between the projects. - If billing is on, trigger a Stripe test checkout and confirm the webhook is received.
Updating a running deployment
Apply new migrations with supabase db push from apps/database before deploying the web or admin build that depends on them. Keep the Supabase key names unchanged; the apps do not read the legacy anon or service-role names. If you change NEXT_PUBLIC_SITE_URL on either app, that app's existing agent connections are bound to the old issuer and users need to reconnect. If you change NEXT_PUBLIC_WEB_APP_URL on the admin app, update the Supabase redirect URLs to match.
For login redirect, email and 401/403 problems after deploying, see troubleshooting.