Deploy to Vercel
ShipVeryFast is a Next.js 15 app, so Vercel is the fastest path to production. This page walks you through importing the repo, wiring up every environment variable, pointing NextAuth and Google OAuth at the deployed domain, registering the Stripe webhook, and smoke-testing auth, checkout, and email after the first deploy.
The boilerplate ships a one-click Vercel button in the README.md. It clones the repo into your Vercel account and prompts you for env vars before the first build. The manual flow below gives you the same result with full control.
Importing the repo and the generated vercel.json
Push the boilerplate to a Git provider (GitHub, GitLab, or Bitbucket), then in the Vercel dashboard choose Add New → Project and import the repository. Vercel auto-detects Next.js and fills in the build settings, so you usually do not need a committed vercel.json at all.
If you want to pin the project settings, the boilerplate can generate one for you. libs/deploymentConfig.ts exports getDefaultVercelConfig(projectName) and generateVercelConfig(config), which produce a vercel.json matching the defaults below (build command, framework, output directory, install command, and the public env keys).
{
"name": "shipveryfast",
"framework": "nextjs",
"buildCommand": "npm run build",
"outputDirectory": ".next",
"installCommand": "npm install"
}The default config also sets regions: ['cdg1'] (Paris) and a Node 18 runtime. Note that generateVercelConfig only writes non-secret env keys into vercel.json, secrets like NEXTAUTH_SECRET and STRIPE_SECRET_KEY are never serialized to the file and must be added in the dashboard.
Setting all required env vars in the Vercel dashboard
ShipVeryFast validates its environment at startup. libs/config.ts runs envSchema.parse(process.env) with Zod, so a missing or malformed required variable throws during the build instead of failing silently at runtime. Add every key below under Project Settings → Environment Variables (scope them to Production, and to Preview if you want preview deploys to run).
| Variable | Required | Notes |
|---|---|---|
SUPABASE_URL | Yes | Validated as a URL. Supabase Dashboard → Project Settings → API. |
SUPABASE_ANON_KEY | Yes | Public anon key. |
SUPABASE_SERVICE_ROLE_KEY | Yes | Server-only admin key. Keep secret. |
NEXTAUTH_SECRET | Yes | Generate with openssl rand -base64 32. |
NEXTAUTH_URL | Yes | Validated as a URL. Set to your deployed domain (see below). |
GOOGLE_CLIENT_ID | Yes | Google Cloud Console OAuth 2.0 client. |
GOOGLE_CLIENT_SECRET | Yes | Same OAuth credential. |
STRIPE_SECRET_KEY | Yes | Stripe → Developers → API keys. |
STRIPE_PUBLIC_KEY | Yes | Stripe publishable key. |
STRIPE_WEBHOOK_SECRET | Yes | From the live webhook endpoint (see below). |
STRIPE_PRICE_BASIC | Yes | Price ID for the Basic plan. |
STRIPE_PRICE_PRO | Yes | Price ID for the Pro plan. |
STRIPE_PRICE_ENTERPRISE | No | Optional, often "contact sales". |
MAILGUN_API_KEY | Yes | Mailgun API key. |
MAILGUN_DOMAIN | Yes | Mailgun sending domain. |
MAILGUN_FROM_EMAIL | Yes | Validated as an email address. |
RATE_LIMIT_MAGICLINK_MAX | Yes | Coerced to a number, e.g. 5. |
RATE_LIMIT_MAGICLINK_DURATION | Yes | Window in seconds, e.g. 3600. |
ANTHROPIC_API_KEY | No | Optional, enables the Claude AI assistant path. |
OPENAI_API_KEY | No | Optional, enables the OpenAI assistant path. |
Two more keys appear in .env.example but are read directly from process.env (not in the Zod schema), so set them too if you use those features: CSRF_SECRET (32+ chars) and ADMIN_EMAILS (a comma-separated allowlist that gates the admin and security routes, it fails closed, so nobody is an admin until you set it).
# Generate strong secrets locally, then paste the output into Vercel
openssl rand -base64 32 # NEXTAUTH_SECRET
openssl rand -base64 32 # CSRF_SECRETSetting NEXTAUTH_URL and updating Google OAuth redirect URIs
Locally NEXTAUTH_URL is http://localhost:3000. In production it must be the canonical URL of the deployed app, with no trailing slash, for example https://your-app.vercel.app or your custom domain. NextAuth builds its OAuth callback URLs from this value, so a mismatch breaks sign-in.
Then update the OAuth client in the Google Cloud Console → APIs & Services → Credentials. Add the deployed callback URL to Authorized redirect URIs. The boilerplate uses the standard NextAuth Google provider (libs/auth.ts), so the callback path is fixed:
# Authorized redirect URIs in the Google OAuth client
https://your-app.vercel.app/api/auth/callback/google
# Keep the local one too, for development
http://localhost:3000/api/auth/callback/googleIf you add a custom domain in Vercel later, set NEXTAUTH_URL to that domain and add its /api/auth/callback/google URI to the Google client as well.
Registering the Stripe webhook endpoint and copying STRIPE_WEBHOOK_SECRET
The webhook handler lives at app/api/webhooks/stripe/route.ts. It verifies the signature with stripe.webhooks.constructEvent and processes subscription and invoice events, so Stripe must be told where to send them.
In the Stripe Dashboard → Developers → Webhooks, add an endpoint pointing at your deployed route and subscribe to the events the handler actually switches on:
# Endpoint URL
https://your-app.vercel.app/api/webhooks/stripe
# Events to send
customer.subscription.updated
invoice.payment_succeeded
invoice.payment_failedAfter creating the endpoint, Stripe shows a Signing secret that starts with whsec_. Copy it into the STRIPE_WEBHOOK_SECRET env var in Vercel and redeploy. The route returns a 400 if the signature does not verify, which is the first thing to check if events show up as failed in the Stripe dashboard.
Build/runtime settings and Node version
Vercel's Next.js preset already matches the boilerplate defaults, there is nothing to override unless you want to pin them. For reference, the generated config in libs/deploymentConfig.ts uses:
| Setting | Value |
|---|---|
| Framework | nextjs |
| Install command | npm install |
| Build command | npm run build |
| Output directory | .next |
| Node version | 18.x (default; 20.x is also supported) |
Set the Node version under Project Settings → General → Node.js Version if you need to pin it. The security headers and Content-Security-Policy in next.config.js only apply when NODE_ENV is production, which is exactly how Vercel builds and serves your app, so HSTS, the CSP, X-Frame-Options, and the other hardening headers are live automatically on a Vercel deploy. The CSP already allowlists js.stripe.com, *.supabase.co, and the Stripe image/API hosts.
Because libs/config.ts parses the environment with Zod, a missing required variable fails the build loudly. If your Vercel build errors out referencing an env key, that is the validator doing its job, add the variable and redeploy.
Verifying auth, checkout, and email after the first deploy
Once the deploy is green, run a quick smoke test against the live domain:
- Auth, open the site and sign in with Google. If you hit a
redirect_uri_mismatch, the redirect URI in the Google client does not matchNEXTAUTH_URL+/api/auth/callback/google. ACLIENT_FETCH_ERRORin the console usually meansNEXTAUTH_SECRETorNEXTAUTH_URLis missing. - Checkout, start a subscription. Checkout sessions are created by
app/api/checkout/session/route.tsusing the price IDs fromSTRIPE_PRICE_BASIC/STRIPE_PRICE_PRO. Complete a test payment, then confirm Stripe delivered thecustomer.subscription.updatedandinvoice.payment_succeededevents to your/api/webhooks/stripeendpoint (check the webhook's recent deliveries in Stripe). - Email, trigger a transactional email (for example via a flow that uses Mailgun). A 401 from Mailgun means
MAILGUN_API_KEYorMAILGUN_DOMAINis wrong; a rejected sender meansMAILGUN_FROM_EMAILis not authorized on your domain.
Check the Functions and Build logs in the Vercel dashboard for any runtime errors during these flows. Once all three pass, your deploy is production-ready.
