Deploy to production
Vercel + Neon production deployment guide
Command Inbox targets Vercel (app) + Neon Postgres (database with pgvector). Gmail webhooks use your production URL — no ngrok in prod.
Documentation deploys as a separate Vercel project at docs.command-inbox.sayantanbal.in.
Prerequisites
- Neon project with Postgres 16+
- Vercel account linked to this repo
- Google Cloud project with Gmail + Calendar OAuth (Testing mode OK for judges)
- GCP Pub/Sub topic for Gmail push
- Pusher Channels app (optional — 5s poll fallback)
- At least one AI key:
OPENAI_API_KEYand/orGOOGLE_GENERATIVE_AI_API_KEY
1. Neon database
- Create a project → copy the pooled connection string
- Enable pgvector (Neon console → Extensions →
vector, or run migrations) - From your machine with production
DATABASE_URL:
bun run db:migrate
bun run corsair:setupKeep CORSAIR_KEK stable — changing it after setup requires bun run corsair:reset and user reconnect.
2. Vercel app project
- Import the GitHub repo in Vercel
- Framework preset: Next.js (
vercel.jsonsetsbun install/bun run build) - Root directory: repository root (main app)
- Add all variables from
.env.example
Production values:
| Variable | Production value |
|---|---|
BETTER_AUTH_URL | https://command-inbox.sayantanbal.in |
APP_URL | https://command-inbox.sayantanbal.in |
DATABASE_URL | Neon pooled connection string |
GMAIL_PUBSUB_TOPIC | projects/<gcp-project>/topics/<topic-name> |
Generate once and store securely:
openssl rand -base64 32 # BETTER_AUTH_SECRET
openssl rand -base64 32 # CORSAIR_KEK
openssl rand -base64 32 # CRON_SECRET- Deploy — migrations are not automatic; run
db:migrateagainst Neon before or immediately after first deploy
3. Google OAuth (production)
In Google Cloud Console → APIs & Services → Credentials:
Authorized JavaScript origins
https://command-inbox.sayantanbal.inAuthorized redirect URIs
https://command-inbox.sayantanbal.in/api/auth/callback/google
https://command-inbox.sayantanbal.in/api/auth/callback/corsairAdd judge emails as Test users while the app stays in Testing mode.
4. Gmail Pub/Sub (production webhooks)
- Create topic + IAM binding per Webhooks & realtime
- Set
GMAIL_PUBSUB_TOPICin Vercel env - Webhook endpoint:
https://command-inbox.sayantanbal.in/api/webhooks?tenantId={userId}Re-run connect flow or bun run gmail:watch against production DB if watches were created with ngrok URLs.
5. Scheduled jobs
Vercel Hobby cron is daily-only. Use an external pinger every 1 minute:
cron-job.org (free)
- URL:
https://command-inbox.sayantanbal.in/api/cron/process-due - Method:
POST - Header:
Authorization: Bearer <CRON_SECRET>
6. Docs site (separate Vercel project)
- Create new Vercel project from same repo
- Set Root Directory to
apps/docs - Add custom domain:
docs.command-inbox.sayantanbal.in - Build uses
apps/docs/vercel.json(installs from monorepo root)
7. Post-deploy smoke test
- Open
/sign-in→ Google sign-in - Connect Gmail + Calendar on
/onboarding/connect - Confirm threads load on
/inbox - Send yourself an email → webhook classifies within ~30s
- Press
/for semantic search after backfill - Press
Mod+Shift+Ffor advanced Gmail search
Troubleshooting
| Symptom | Fix |
|---|---|
| 500 on sign-in | BETTER_AUTH_SECRET, BETTER_AUTH_URL, OAuth redirect URIs |
| Empty inbox after connect | Run corsair:setup against prod DB |
| Webhooks never fire | Pub/Sub IAM; GMAIL_PUBSUB_TOPIC; watch uses prod APP_URL |
| AI features 503 | Set AI keys in Vercel |
| Send-later never sends | Configure cron-job.org with CRON_SECRET |
Optional: deploy from CLI
npm i -g vercel
vercel login
vercel link
vercel env pull .env.production.local
vercel --prodMigrations always run locally/CI against DATABASE_URL, not inside the Vercel build.