ShipVeryFastShipVeryFast
Documentation

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).

VariableRequiredNotes
SUPABASE_URLYesValidated as a URL. Supabase Dashboard → Project Settings → API.
SUPABASE_ANON_KEYYesPublic anon key.
SUPABASE_SERVICE_ROLE_KEYYesServer-only admin key. Keep secret.
NEXTAUTH_SECRETYesGenerate with openssl rand -base64 32.
NEXTAUTH_URLYesValidated as a URL. Set to your deployed domain (see below).
GOOGLE_CLIENT_IDYesGoogle Cloud Console OAuth 2.0 client.
GOOGLE_CLIENT_SECRETYesSame OAuth credential.
STRIPE_SECRET_KEYYesStripe → Developers → API keys.
STRIPE_PUBLIC_KEYYesStripe publishable key.
STRIPE_WEBHOOK_SECRETYesFrom the live webhook endpoint (see below).
STRIPE_PRICE_BASICYesPrice ID for the Basic plan.
STRIPE_PRICE_PROYesPrice ID for the Pro plan.
STRIPE_PRICE_ENTERPRISENoOptional, often "contact sales".
MAILGUN_API_KEYYesMailgun API key.
MAILGUN_DOMAINYesMailgun sending domain.
MAILGUN_FROM_EMAILYesValidated as an email address.
RATE_LIMIT_MAGICLINK_MAXYesCoerced to a number, e.g. 5.
RATE_LIMIT_MAGICLINK_DURATIONYesWindow in seconds, e.g. 3600.
ANTHROPIC_API_KEYNoOptional, enables the Claude AI assistant path.
OPENAI_API_KEYNoOptional, 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_SECRET

Setting 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/google

If 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_failed

After 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:

SettingValue
Frameworknextjs
Install commandnpm install
Build commandnpm run build
Output directory.next
Node version18.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:

  1. 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 match NEXTAUTH_URL + /api/auth/callback/google. A CLIENT_FETCH_ERROR in the console usually means NEXTAUTH_SECRET or NEXTAUTH_URL is missing.
  2. Checkout, start a subscription. Checkout sessions are created by app/api/checkout/session/route.ts using the price IDs from STRIPE_PRICE_BASIC / STRIPE_PRICE_PRO. Complete a test payment, then confirm Stripe delivered the customer.subscription.updated and invoice.payment_succeeded events to your /api/webhooks/stripeendpoint (check the webhook's recent deliveries in Stripe).
  3. Email, trigger a transactional email (for example via a flow that uses Mailgun). A 401 from Mailgun means MAILGUN_API_KEY or MAILGUN_DOMAIN is wrong; a rejected sender means MAILGUN_FROM_EMAIL is 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.

Next steps