---
title: "Conditional Build Commands: Environment, Branch, and Custom Workflows"
description: Run a different Vercel build command for each environment or Git branch using a shell script, vercel.json, or vercel.ts, and see how Vercel picks one.
url: /kb/guide/dynamic-build-commands
canonical_url: "https://vercel.com/kb/guide/dynamic-build-commands"
published: 2025-11-03
last_updated: 2026-09-02
authors: DX Team
related:
  - /docs/environment-variables/system-environment-variables
  - /docs/deployments/environments
  - /docs/project-configuration/vercel-ts
  - /kb/guide/how-do-i-use-the-ignored-build-step-field-on-vercel
  - /docs/deployments/logs
  - /docs/builds/configure-a-build
  - /docs/project-configuration/vercel-json
  - /docs/monorepos
  - /docs/cli/build
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---
<!-- docsgraph:related -->
## Related pages

> **For AI agents:** Follow these links to understand how this page connects to the rest of the Vercel ecosystem. For the full cross-link map (inbound, outbound, prerequisites, and semantic neighbors), see the .graph.md link below.

- [Builds](https://vercel.com/docs/builds?from=related&source_path=%2Fkb%2Fguide%2Fdynamic-build-commands&source_site=vercel-kb&relationship=related) — Understand how the build step works when creating a Vercel Deployment.
- [Deploying Git Repositories with Vercel](https://vercel.com/docs/git?from=related&source_path=%2Fkb%2Fguide%2Fdynamic-build-commands&source_site=vercel-kb&relationship=related) — Vercel automatically deploys supported Git repositories on every branch push and when changes merge into the production
- [Deploying a project from the CLI](https://vercel.com/docs/projects/deploy-from-cli?from=related&source_path=%2Fkb%2Fguide%2Fdynamic-build-commands&source_site=vercel-kb&relationship=related) — Set up and deploy a Vercel project using the CLI, from linking to production.
- [How Vercel builds your application](https://vercel.com/docs/fundamentals/builds?from=related&source_path=%2Fkb%2Fguide%2Fdynamic-build-commands&source_site=vercel-kb&relationship=related) — Learn how Vercel transforms your source code into optimized assets ready to serve globally.
- [Troubleshooting Build Errors](https://vercel.com/docs/deployments/troubleshoot-a-build?from=related&source_path=%2Fkb%2Fguide%2Fdynamic-build-commands&source_site=vercel-kb&relationship=related) — Learn how to resolve common scenarios you may encounter during the Build step, including build errors that cancel a depl
- [Per-environment and per-branch Build Commands on Vercel](https://vercel.com/kb/guide/per-environment-and-per-branch-build-commands?from=related&source_path=%2Fkb%2Fguide%2Fdynamic-build-commands&source_site=vercel-kb&relationship=related) — Customize your commands for specific behaviors based on branch, environment, and more.
- [Deploy to Vercel with Self-Hosted Git Pipelines \\(GitLab & Bitbucket\\)](https://vercel.com/kb/guide/how-can-i-use-gitlab-pipelines-with-vercel?from=related&source_path=%2Fkb%2Fguide%2Fdynamic-build-commands&source_site=vercel-kb&relationship=related) — Learn how to use GitLab Pipelines to deploy to Vercel including support for self-managed GitLab.
- [How to use a non-default branch for production deployments on Vercel](https://vercel.com/kb/guide/can-i-use-a-non-default-branch-for-production?from=related&source_path=%2Fkb%2Fguide%2Fdynamic-build-commands&source_site=vercel-kb&relationship=related) — Learn how to set a non-default branch for production on Vercel. Open the Production environment, change branch tracking,
- [How to add and manage environment variables on Vercel](https://vercel.com/kb/guide/how-to-add-vercel-environment-variables?from=related&source_path=%2Fkb%2Fguide%2Fdynamic-build-commands&source_site=vercel-kb&relationship=related) — Add environment variables to Vercel through the dashboard, CLI, or REST API, scope them to each environment, and pull th
- [Why aren't commits triggering deployments on Vercel?](https://vercel.com/kb/guide/why-aren-t-commits-triggering-deployments-on-vercel?from=related&source_path=%2Fkb%2Fguide%2Fdynamic-build-commands&source_site=vercel-kb&relationship=related) — Commits not triggering deployments on Vercel? Walk the diagnostic checklist covering authentication, commit author acces

Full cross-link map for this page: [/kb/guide/dynamic-build-commands.graph.md](/kb/guide/dynamic-build-commands.graph.md?from=related&source_path=%2Fkb%2Fguide%2Fdynamic-build-commands&source_site=vercel-kb&relationship=graph)
<!-- /docsgraph:related -->


## How to run a different Vercel build command per environment or branch

Vercel runs one build command per deployment, but that command doesn't have to be the same everywhere. Point it at a script that reads the deployment's environment or branch. A single project can then run `npm run build:production` for production and `npm run build:preview` for every preview.

Here's how Vercel decides which command to run, and how to make that decision depend on the environment, the Git branch, or your own environment variables.

## How Vercel resolves your build command

Three places can set the build command, and each one overrides the ones below it:

1. `**vercel.json**` **or** `**vercel.ts**`**:** A `buildCommand` property applies to deployments built from that commit, so it can differ per branch.
   
2. **Project Settings:** The **Override** toggle under **Settings** > **Build and Deployment** applies to every deployment in the project.
   
3. **The framework preset:** Vercel picks a default based on the framework you're deploying.
   

If Next.js is your framework, Vercel looks for a `build` script in `scripts` and runs it, and falls back to `next build` if there isn't one. So `npm run build` ends up being the effective command even though nothing on Vercel names it.

A dynamic build command is a deliberate use of the top two layers. You override the default with a command that runs a script, and the script decides the rest.

## How to run different build commands per environment

Start with the two npm scripts your build will choose between, defined in `package.json`:

```json
{
  "scripts": {
    "build:production": "next build",
    "build:preview": "echo \"Building a preview\" && next build"
  }
}
```

Next, create a `vercel.sh` file in your project's root directory that branches on the environment.

Use `VERCEL_ENV`, which is set to `production`, `preview`, or `development` on every deployment:

```bash
#!/bin/bash
if [[ $VERCEL_ENV == "production" ]] ; then
  npm run build:production
else
  npm run build:preview
fi
```

The script can be written in Bash or Node.js, and it has access to every [system environment variable](https://vercel.com/docs/environment-variables/system-environment-variables) Vercel exposes at build time.

If your project uses [custom environments](https://vercel.com/docs/deployments/environments#custom-environments) such as staging or QA, `VERCEL_ENV` reports those as `preview`. Read `VERCEL_TARGET_ENV` instead, which returns `production`, `preview`, `development`, or the name of the custom environment.

Finally, point the build command at the script. You can do this in your project configuration file or in the dashboard.

Set `buildCommand` in `vercel.json`:

```json
{
  "$schema": "<https://openapi.vercel.sh/vercel.json>",
  "buildCommand": "sh vercel.sh"
}
```

To do the same in the dashboard, select your project and open **Settings** > **Build and Deployment**. Turn on the **Override** toggle for **Build Command** and enter `sh` [`vercel.sh`](http://vercel.sh).

Both routes run the same script. Pick `vercel.json` when the command should differ per branch, and the dashboard when it shouldn't.

## How to set a per-branch build command on Vercel

Matching on the branch name varies the build per branch, without configuring a custom environment for each one.

Read `VERCEL_GIT_COMMIT_REF`, which holds the Git branch of the commit that triggered the deployment:

```bash
#!/bin/bash
if [[ $VERCEL_GIT_COMMIT_REF == "main" ]] ; then
  npm run build:production
elif [[ $VERCEL_GIT_COMMIT_REF == "staging" ]] ; then
  npm run build:staging
else
  npm run build:preview
fi
```

Use `VERCEL_GIT_COMMIT_REF` rather than `VERCEL_BRANCH_URL` for this. `VERCEL_BRANCH_URL` holds the generated branch domain, such as `my-site-git-improve-about-page.vercel.app`, so matching on it means parsing a hostname instead of comparing a branch name.

### Environment and branch settings in the dashboard

Matching on `VERCEL_TARGET_ENV` depends on the dashboard configuration. A branch only reports a custom environment's name if that environment tracks it, so open **Settings** > **Environments**, select the environment, and set the branch under **Branch Tracking**. Without that, the branch deploys as a preview and your script takes the `preview` path.

## Build command recipes for monorepos and generated config

Three setups need more than an environment or branch check:

- **Switch on your own variable:** Add a variable such as `BUILD_PROFILE` in **Settings** > **Environment Variables**, scope it per environment or per branch, and branch on it in [`vercel.sh`](http://vercel.sh). The script then stays stable while the routing lives in the project configuration.
  
- **Give each monorepo app its own command:** Connect each app as a separate Vercel project, set its **Root Directory** under **Settings** > **Build and Deployment**, and give that project its own build command. Each project builds independently from the same repository.
  
- **Compute the configuration itself:** Use [`vercel.ts`](https://vercel.com/docs/project-configuration/vercel-ts) instead of `vercel.json`. It executes at build time rather than being static, so the configuration can be generated rather than hardcoded.
  

A `vercel.ts` file exports a typed `config` object and accepts the same property names as `vercel.json`:

```typescript
import type { VercelConfig } from '@vercel/config/v1';

export const config: VercelConfig = {
  buildCommand: 'sh vercel.sh',
};
```

Use one configuration file or the other. A project should have `vercel.ts` or `vercel.json`, not both.

## When to skip a build instead of changing the build command

Some cases don't call for a different command at all. To stop a commit from building, use the Ignored Build Step rather than exiting early from [`vercel.sh`](http://vercel.sh). A build command that exits cleanly still produces a deployment, while the Ignored Build Step cancels it.

The Ignored Build Step runs a command of your choosing when the deployment enters the `BUILDING` state. It runs from the project's Root Directory and has access to system environment variables. An exit code of 1 continues the build, and an exit code of 0 cancels it and sets the deployment state to `CANCELED`.

Set it under **Settings** > **Build and Deployment**, or override it per deployment with `ignoreCommand` in `vercel.json` or `vercel.ts`.

Canceled builds still count as full deployments against your deployment quota and concurrent build slots. For the script patterns and monorepo examples, see the [Ignored Build Step guide](https://vercel.com/kb/guide/how-do-i-use-the-ignored-build-step-field-on-vercel).

## How to troubleshoot a Vercel build command that isn't running

When the script doesn't take effect, the cause is usually configuration rather than the script itself:

- **Permission denied when the script runs:** Set the build command to `sh`[`vercel.sh`](http://vercel.sh) rather than `./`[`vercel.sh`](http://vercel.sh). Running the file through `sh` means its executable bit doesn't need to be committed to Git.
  
- **The old command still runs:** Build and Deployment settings apply from the next deployment onward. Open the **Deployments** tab and select **Redeploy** to pick up the change.
  
- **Every deployment takes the** `**else**` **branch:** Your script is reading an empty variable. Open **Settings** > **Environment Variables** and select **Enable access to System Environment Variables**, which exposes `VERCEL_ENV` and `VERCEL_GIT_COMMIT_REF` to the build.
  
- **The script isn't found in a monorepo:** A build can't reach files outside its **Root Directory**. Move [`vercel.sh`](http://vercel.sh) inside that directory rather than leaving it at the repository root.
  
- **Your settings are ignored entirely:** A `builds` array in `vercel.json` makes Vercel ignore your Build and Deployment settings. Remove `builds` and use the `functions` property instead.
  

If the script runs but takes the wrong branch, echo the variable you're matching on and read the value back in your [build logs](https://vercel.com/docs/deployments/logs).

## Next steps

With the build command routing correctly, the same repository can serve preview and production builds from one project. Start a [new Vercel project](https://vercel.com/new) to set this up from the beginning, or [browse the templates](https://vercel.com/templates) for a framework-ready starting point.

## Related resources

- [Configuring a build](https://vercel.com/docs/builds/configure-a-build#build-command)
  
- [Static configuration with vercel.json](https://vercel.com/docs/project-configuration/vercel-json)
  
- [Programmatic configuration with vercel.ts](https://vercel.com/docs/project-configuration/vercel-ts)
  
- [System environment variables](https://vercel.com/docs/environment-variables/system-environment-variables)
  
- [How do I use the "Ignored Build Step" field on Vercel?](https://vercel.com/kb/guide/how-do-i-use-the-ignored-build-step-field-on-vercel)
  
- [Using monorepos](https://vercel.com/docs/monorepos)
  

## Frequently asked questions

### What is the difference between the build command and the framework preset?

The framework preset tells Vercel which framework you're deploying and supplies defaults for the build command, output directory, and install command. The build command is one of those defaults. Changing the preset changes several settings at once, while overriding the build command changes only what Vercel runs to build your project.

### Can I override the Vercel build command for a single deployment?

Yes. Add `buildCommand` to `vercel.json` or `vercel.ts` on the branch you're deploying, and it takes precedence over the Build Command in Project Settings. Because the file is versioned alongside your code, each branch can carry its own configuration without any dashboard change.

### Which environment variable tells my script which branch is building?

Match on `VERCEL_GIT_COMMIT_REF`, which carries the branch name and is available at both build and runtime. For the environment rather than the branch, use `VERCEL_ENV` for `production`, `preview`, or `development`, or `VERCEL_TARGET_ENV` when you need the name of a custom environment, such as staging.

### Can I test my build command locally before deploying?

Run `vercel pull` to fetch your Project Settings and environment variables, then [`vercel build`](https://vercel.com/docs/cli/build) to run the same build command Vercel would. Add `--target=staging` to build against a specific environment, or `--prod` to use Production environment variables instead of the Preview defaults.