Gating Vercel auto-deploys per branch with vercel.json
Pushes to our staging branch kept creating throwaway preview deploys. One git.deploymentEnabled block in vercel.json ended it.
TL;DR
Vercel deploys every pushed branch by default, so a repo with main and stag ends up with redundant preview deployments. An eight-line vercel.json using git.deploymentEnabled silences stag while keeping main on production. It beats the Ignored Build Step because declarative JSON can't invert exit codes or rot, just avoid overlapping glob rules.
The morning I migrated our deploys, I was so focused on which branch Vercel treats as production that I almost missed the other problem: pushes to stag kept producing preview deployments nobody asked for.
Context first. Our company website repo deploys to Vercel with main as the production branch, while stag is the staging branch that gets merged into main later. Vercel, by default, deploys every branch you push. With two long-lived branches, half of those deployments were previews of commits I had already reviewed on staging. Wasted build minutes, a noisy dashboard, and a real risk of confusing which URL is which. I was sure fixing this meant fiddling with dashboard settings or writing a build-skip script. Both seemed heavy for what is, in the end, a routing preference.
The actual fix fits in eight lines of JSON. A new vercel.json at the repo root:
{
"git": {
"deploymentEnabled": {
"main": true,
"stag": false
}
}
}
That's the whole file. The git.deploymentEnabled key takes a map of branch names to booleans; commits to a branch set to false simply do not trigger a deployment, and every unspecified branch defaults to true [4]. So stag: false silences staging pushes, while main keeps deploying production on merge.
The redundant line I kept on purpose
The explicit main: true is technically redundant, since unspecified branches already default to true. I kept it anyway. The file now states the intent for both branches in one glance, and nobody has to infer the implicit default while debugging at 1 a.m. Config files double as documentation, but only when you let them say the obvious parts out loud.
One behavior worth knowing before you get clever with patterns: when a branch matches multiple rules and at least one of them is true, a deployment happens [4]. The setting supports glob patterns like internal-*: false, but the resolution is any-true-wins, not last-match-wins. Write your rules so they don't overlap, or the branch you meant to silence will quietly keep deploying.
Why I skipped the Ignored Build Step
I considered the Ignored Build Step, and I'm glad I didn't start there. It's a different mechanism: a user-defined script where a return code of 0 skips the build and a code of 1 or greater builds a new deployment [5]. That's the exact opposite of Unix exit-code convention, and Vercel's own troubleshooting guide lists inverted-logic scripts as a classic reason commits mysteriously stop deploying. A declarative JSON flag can't be mis-exited the wrong way, and it can't rot the way a shell snippet does when someone refactors the CI around it.
There is one trap from the other direction, and it's documented in the same guide: when commits stop triggering deployments and nobody remembers why, checking for git.deploymentEnabled set to false is on the official diagnostic checklist [5]. Gating you set once and forget becomes a mystery for whoever debugs next, and that someone is usually future you. Our vercel.json lives in the repo, gets reviewed like code, and the commit message says exactly what it does and why.
For context, the older github.enabled property that some repositories still carry has been deprecated in favor of this exact key [4]. If your repo predates the rename, migrating is a two-line diff and there is no reason to wait.
Two long-lived branches now behave like two environments instead of four deploy targets. Sometimes the right config is the one small enough to read entirely in the commit diff.
Sources
[4] https://vercel.com/docs/project-configuration/git-configuration
[5] https://vercel.com/kb/guide/why-aren-t-commits-triggering-deployments-on-vercel