Configure production env on Vercel for mkan v1.0
This issue tracks the env vars that need to be set on Vercel Production (and optionally Preview) before / after merging PR #2.
src/lib/env.ts validates these at first runtime call, so a missing required var fails fast with a readable error. Optional vars degrade gracefully (Stripe key absent → only Stripe paths fail; cash + cron flows still work).
Required (build & runtime)
| Key |
What it does |
Where to get it |
DATABASE_URL |
Neon PostgreSQL connection string. |
Neon dashboard → project → Connection string (pooled, with ?sslmode=require). |
AUTH_SECRET |
NextAuth v5 JWT signing secret. ≥ 32 bytes. |
Generate with openssl rand -base64 32. |
NEXTAUTH_URL |
Public origin for callback URLs. |
https://mkan.databayt.org (or your prod domain). |
IMAGEKIT_PRIVATE_KEY |
ImageKit server-side uploads + DELETE. |
ImageKit dashboard → Developer → API keys. |
NEXT_PUBLIC_IMAGEKIT_PUBLIC_KEY |
Client-side ImageKit upload init. |
Same dashboard. |
NEXT_PUBLIC_IMAGEKIT_URL_ENDPOINT |
CDN base for transforms. |
https://ik.imagekit.io/<your-id>. |
RESEND_API_KEY |
Transactional email (verify, 2FA, booking confirmation, trip cancelled). |
Resend dashboard → API Keys → Create. |
EMAIL_FROM |
Sender address. Must match a verified domain in Resend. |
e.g. noreply@mkan.databayt.org. |
UPSTASH_REDIS_REST_URL |
Rate limiting (auth/upload/search/payment/general tiers). |
Upstash console → database → REST URL. |
UPSTASH_REDIS_REST_TOKEN |
Same. |
Upstash console → database → REST Token. |
Recommended (live features depend on these)
| Key |
What breaks without it |
Where to get it |
STRIPE_SECRET_KEY |
Card payments via Stripe (homes lease + transport). Use live key for prod, test for preview. |
Stripe dashboard → Developers → API keys → Secret key. |
STRIPE_WEBHOOK_SECRET |
Webhook signature verification at /api/webhooks/stripe. Without it, webhook returns 400. |
Stripe → Developers → Webhooks → Add endpoint https://<domain>/api/webhooks/stripe, copy the signing secret. |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY |
Client-side Stripe.js for the card form. |
Stripe dashboard → publishable key. |
NEXT_PUBLIC_MAPBOX_TOKEN |
Listing detail interactive map. (We fall back to OpenStreetMap iframe if absent.) |
Mapbox account → Access tokens. |
Optional (auth providers + features)
| Key |
Effect if missing |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET |
Google OAuth login button hides / errors. Credentials login still works. |
FACEBOOK_CLIENT_ID / FACEBOOK_CLIENT_SECRET |
Same for Facebook. |
GITHUB_PERSONAL_ACCESS_TOKEN / GITHUB_REPO |
Report-issue feature can't auto-file to GitHub. |
CRON_SECRET |
/api/cron/{release-seats,mark-overdue,generate-monthly} accept calls without a secret check. Vercel cron sends a header you can validate. Recommended in prod. |
NEXT_PUBLIC_APP_URL |
Defaults to http://localhost:3000. Set to prod domain so emails link correctly. |
How to set them on Vercel
- Open the project on Vercel → Settings → Environment Variables.
- Add each key. Tick Production (and optionally Preview).
- Redeploy the latest production deployment so the new env is picked up (Vercel doesn't apply env changes to existing builds — only new ones).
# Or via CLI (requires `vercel link` + login):
vercel env add DATABASE_URL production
vercel env add AUTH_SECRET production
# ...
vercel --prod # trigger fresh deploy
Verification checklist (after setting)
Stripe webhook setup (gotcha)
The webhook endpoint must be added to Stripe after the prod URL is reachable:
- Deploy to Vercel first (without
STRIPE_WEBHOOK_SECRET is fine — endpoint will 400 until it's set, but won't crash).
- In Stripe → Webhooks → Add endpoint → URL =
https://<domain>/api/webhooks/stripe.
- Select events:
payment_intent.succeeded, payment_intent.payment_failed, charge.refunded.
- Copy the new endpoint's signing secret.
- Set it as
STRIPE_WEBHOOK_SECRET in Vercel.
- Redeploy.
🤖 Generated with Claude Code
Configure production env on Vercel for mkan v1.0
This issue tracks the env vars that need to be set on Vercel Production (and optionally Preview) before / after merging PR #2.
src/lib/env.tsvalidates these at first runtime call, so a missing required var fails fast with a readable error. Optional vars degrade gracefully (Stripe key absent → only Stripe paths fail; cash + cron flows still work).Required (build & runtime)
DATABASE_URL?sslmode=require).AUTH_SECRETopenssl rand -base64 32.NEXTAUTH_URLhttps://mkan.databayt.org(or your prod domain).IMAGEKIT_PRIVATE_KEYNEXT_PUBLIC_IMAGEKIT_PUBLIC_KEYNEXT_PUBLIC_IMAGEKIT_URL_ENDPOINThttps://ik.imagekit.io/<your-id>.RESEND_API_KEYEMAIL_FROMnoreply@mkan.databayt.org.UPSTASH_REDIS_REST_URLUPSTASH_REDIS_REST_TOKENRecommended (live features depend on these)
STRIPE_SECRET_KEYSTRIPE_WEBHOOK_SECRET/api/webhooks/stripe. Without it, webhook returns 400.https://<domain>/api/webhooks/stripe, copy the signing secret.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYNEXT_PUBLIC_MAPBOX_TOKENOptional (auth providers + features)
GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRETFACEBOOK_CLIENT_ID/FACEBOOK_CLIENT_SECRETGITHUB_PERSONAL_ACCESS_TOKEN/GITHUB_REPOCRON_SECRET/api/cron/{release-seats,mark-overdue,generate-monthly}accept calls without a secret check. Vercel cron sends a header you can validate. Recommended in prod.NEXT_PUBLIC_APP_URLhttp://localhost:3000. Set to prod domain so emails link correctly.How to set them on Vercel
Verification checklist (after setting)
https://<domain>/api/healthreturns 200 with valid JSON.https://<domain>/en/listingsand/ar/listingsrender.https://<domain>/en/transportand/ar/transportrender./api/webhooks/stripereceives apayment_intent.succeededevent from Stripe CLI / Dashboard test trigger and returns 200.Stripe webhook setup (gotcha)
The webhook endpoint must be added to Stripe after the prod URL is reachable:
STRIPE_WEBHOOK_SECRETis fine — endpoint will 400 until it's set, but won't crash).https://<domain>/api/webhooks/stripe.payment_intent.succeeded,payment_intent.payment_failed,charge.refunded.STRIPE_WEBHOOK_SECRETin Vercel.🤖 Generated with Claude Code