This was a Convex Next.js app with two deploy paths
Next.js on Vercel. Convex backend. GitHub for source. For months we ran a custom GitHub Actions workflow that called Vercel APIs, posted preview comments, and ran a TypeScript build wrapper before deploy. Two systems. Two logs. When preview broke, nobody knew which side lied.
What they would notice if it failed
A green GitHub check and a red Vercel deployment. Or the opposite. Preview URLs that never showed up in the PR thread because the comment helper failed after the deploy succeeded. Or Convex schema changes that shipped to staging but not production because the wrapper script ran in CI but not on the Vercel build that actually served traffic.
Custom deploy duplicated what Git already does
We maintained deploy.yml, preview-comment helpers, apps/web/vercel.json, and vercelBuild.ts. The workflow fought Vercel's native Git integration. Every push triggered Actions. Actions triggered Vercel. Vercel also triggered itself from the same push. Race conditions. Duplicate builds. Secrets in two places.
The real puzzle was not "how do we deploy faster." It was "why are we deploying twice."
Vercel Git deploy already knows the branch, the commit, and the preview URL. Convex needs to run npx convex deploy as part of the frontend build when schema or functions change. That belongs in the Vercel dashboard Build Command, not in a sidecar workflow that can pass while production skips the backend step.
I deleted the workflow. Removed the preview-comment plumbing. Removed the local vercel.json override and the TS build script. Docs now say: set Build Command in Vercel to wrap the Next.js build with Convex deploy. One pipeline. One log stream. One place to read why preview failed.
# Vercel dashboard Build Command (conceptual)
npx convex deploy --cmd 'npm run build'GitHub Actions is great for lint and test. It is a bad second deploy button for an app that already deploys on Git push. If Vercel owns the hosting graph, let Vercel own the build graph too. Convex rides along inside that command. No orphan backend.
The lingering thought: if your CI deploy and your host deploy can both succeed independently, you've built a split-brain release. Pick one spine.
Happy coding! Sander