Skip to main content

September 1, 2026

Convex Vercel deploy without GitHub Actions

Convex Vercel deploy wins over a parallel Actions pipeline. One host build runs Convex on preview and production; GitHub Actions stays on tests only.

Sander Korf4 min read
vercelconvexgithub-actionsnextjs

My Convex-backed Next.js app on Vercel had two deploy paths for a single Git push. GitHub held the source. For months a custom GitHub Actions workflow called Vercel APIs, posted preview comments, and ran a TypeScript build wrapper before deploy. Vercel Git also built from the same commit. When preview broke, I could not tell which log was lying.

Preview and production disagreed in ugly ways. A green GitHub check beside a red Vercel deployment, or the reverse. Preview URLs missing from the PR thread because the comment helper failed after Vercel had already shipped. Convex schema changes on staging that never reached production because the wrapper ran in CI while the host build that served traffic skipped the backend step.

One push, two spines

The sidecar lived in deploy.yml, plus preview-comment helpers, a local apps/web/vercel.json, and vercelBuild.ts. Each push started Actions. Actions nudged Vercel. Vercel Git had already started from the same commit.

Preview comments are notifications. When the helper died, the PR looked undeployed while a URL already existed. Actions could stay green while the build users hit failed. Two pipelines could succeed independently. That is how staging schema and production functions drift apart.

I kept chasing the wrong failure. I would open the Actions log, see Convex deploy succeed, and assume the preview URL matched production. The Vercel build tab sometimes never ran convex deploy at all because the dashboard Build Command still pointed at plain npm run build. CI had become a rehearsal, not the show.

Let the host own the build graph

GitHub Actions is a fine place to lint and test. It is a poor second deploy button when the app already deploys on Git push. I wanted comments, a TypeScript wrapper, and a feeling of control. The wrapper hid the real failure: Convex could run in CI and skip the build that served traffic.

Vercel Git deploy already knows branch, commit, and preview URL. I needed one log stream that explained preview failures, including the backend step. Keeping vercel.json in the app while the dashboard Build Command also pointed somewhere else was another fork. Local overrides and the TS script meant remembering which file was authoritative that week.

Deleting the sidecar felt like giving up guardrails. It was giving up duplicate guardrails. Lint and test still block merges. They just stopped shipping a second copy of the app behind Vercel's back.

Convex on the Vercel build command

Convex needs 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 that can pass while production skips the backend.

I deleted the deploy workflow, preview-comment plumbing, 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.

# Vercel dashboard Build Command (conceptual)
npx convex deploy --cmd 'npm run build'

--cmd ties Convex deploy and the Next.js build into one command on the host that serves the app. Schema or function changes ride along. If the command fails, the deployment fails. Staging cannot swallow a CI success that production never ran.

GitHub Actions still runs lint and test. Those jobs fail the PR. They do not ship, invent preview URLs, or hold a second copy of deploy secrets.

Pick one spine for the release

If Vercel owns hosting, let Vercel own the build graph too. Convex rides inside that command. Preview URLs come from Vercel because Vercel made them. GitHub answers whether the code may ship, not whether it shipped twice.

If your CI deploy and your host deploy can both succeed independently, you have built a split-brain release. Pick one spine. I picked the host. Actions still run. They just stopped pretending to be a second Vercel.


Happy coding! Sander