---
title: How to reduce build costs on Vercel
description: Find which projects drive your Build CPU Minutes, then lower them by skipping unneeded builds, shortening each build, and right-sizing build machines.
url: "https://vercel.com/kb/guide/how-reduce-build-costs"
published: 2026-11-03
last_updated: 2026-11-03
authors: Nic Dillon
related_resources:
  - title: "How do I reduce my build time with Next.js on Vercel?"
    url: "/kb/guide/how-do-i-reduce-my-build-time-with-next-js-on-vercel"
    description: "Reduce Next.js build times on Vercel by pre-rendering fewer pages at build time, deferring generation with ISR and image optimization, and using faster build machines."
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

Vercel bills build compute in Build CPU Minutes. Each build's billable duration, which is build time plus post-build time, is rounded up to the next whole minute and multiplied by the number of vCPUs on its build machine. Your total is the sum across all builds, so there are three ways to lower it: run fewer builds, make each build shorter, or use fewer vCPUs.

A faster build on a bigger machine can cost more. If your goal is faster builds rather than cheaper ones, start with [How do I reduce my build time with Next.js on Vercel?](https://vercel.com/kb/guide/how-do-i-reduce-my-build-time-with-next-js-on-vercel).

> Build machine selection and on-demand concurrency require a Pro or Enterprise team and the [Owner role](https://vercel.com/docs/rbac/access-roles#owner-role).

## How Build CPU Minutes are calculated

Vercel charges $0.0035 per CPU minute on every build machine, so a larger machine costs more per minute only because it has more vCPUs. Hobby teams always build on Basic. Pro and Enterprise teams can choose Basic, Standard, Enhanced, Turbo, or Elastic, with Elastic as the default for new Pro and Enterprise teams. See the latest [pricing on Vercel.](https://vercel.com/docs/pricing#builds)

| Machine type | vCPUs                | Memory (GB) | Disk (GB)   |
| ------------ | -------------------- | ----------- | ----------- |
| Basic        | 2                    | 8           | 32          |
| Standard     | 4                    | 8           | 32          |
| Enhanced     | 8                    | 16          | 64          |
| Turbo        | 30                   | 60          | 64          |
| Elastic      | 4 to 30, auto-scaled | 8 to 60     | Auto-scaled |

A bigger machine lowers Build CPU Minutes only if it cuts build time by at least as much as it adds vCPUs, and rounding up to the next minute makes that harder. A 3-minute build on Standard costs 12 CPU minutes, because 3 × 4 = 12. On Enhanced, the same build must finish in 1 minute to cost less, at 1 × 8 = 8. If it finishes in 1 minute 30 seconds, Vercel rounds it up to 2 minutes and bills 16 CPU minutes, which is more than Standard even though the build is faster.

Start by finding what's driving your usage, then apply the steps that match what you find.

## Find what's driving your usage

Run these checks on your highest-usage projects, then use the table after the list to choose a fix.

1. **Rank projects by usage**: Open **Usage**, go to the **Builds** section, and view it by project. Compare windows of equal length, such as two full weeks against two full weeks, rather than a partial month against a full one. From the CLI, running `vercel usage --since 30d --group-by project` gives the same per-project view. Learn more in [Manage and optimize usage](https://vercel.com/docs/pricing/manage-and-optimize-usage).
   
2. **Find the slowest build phase**: In the project, open **Observability**, then **Build Diagnostics**. It shows build duration, memory and disk usage, and time per build step, so you can tell whether clone, install, or the build command takes the longest.
   
   When a build hits its memory or disk limit, Vercel adds a system report to the build logs. To get the report on every build, set the `VERCEL_BUILD_SYSTEM_REPORT` environment variable to `1`. The report shows hidden out-of-memory events, breaks down disk usage by source code, `node_modules`, and build output, and flags files over 100 MB. Learn more in [Troubleshooting Build Errors](https://vercel.com/docs/deployments/troubleshoot-a-build#build-container-resources).
   
3. **Check individual deployments**: Each deployment's details page shows its billable duration and CPU minutes. From the Vercel CLI, `vercel inspect <deployment>` shows the same figures.
   
4. **Check the build cache size**: The deployment summary shows how large a build cache the deployment saved and how close it is to the limit for its build machine. With Vercel CLI, `vercel inspect` shows the same. Uploading the cache counts as billable build time, and Vercel does not save a build cache that exceeds the limit.
   
5. **Count failed builds**: Filter the project's **Deployments** list by the **Error** status, or select **Ratio** in the **Builds** section of **Usage** to compare completed and errored builds. A project that fails often can cost more than a large project that builds cleanly, because Vercel bills every retry as a full build.
   
6. **Check the build machine setting**: In team **Settings**, open **Build and Deployment**, then **Build Machines**, and check each high-usage project's own setting too. Note any project pinned to Enhanced or Turbo. Teams created before Elastic became the default may still have projects on a fixed machine.
   
7. **Check Turborepo cache hits**: If you use Turborepo, open a recent deployment and select **Run Summary** next to the **Building** section. It shows each task's cache status. When a previous deployment from the same branch exists, it also shows which inputs changed and caused a miss. Learn more in [Deploying Turborepo to Vercel](https://vercel.com/docs/monorepos/turborepo#unexpected-cache-misses).
   

### Match findings to a fix

| What you see                                                 | What to do                                                                        | How to confirm it worked                                            |
| ------------------------------------------------------------ | --------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| Every monorepo project builds on every commit                | Skip unaffected projects, or use `turbo query affected`                           | A commit to one app builds only that app                            |
| Previews build for branches nobody reviews                   | Turn off deployments for those branches, or build previews only for pull requests | Previews appear only for the branches or pull requests you kept     |
| A bot or coding agent pushes bursts of commits to one branch | Run one build per branch                                                          | Fewer builds than commits during a burst                            |
| A CMS webhook calls a Deploy Hook on every publish           | Revalidate content instead of rebuilding                                          | Publishing no longer creates a deployment                           |
| Failed builds get redeployed                                 | Fix the recurring error                                                           | Fewer errored deployments over an equal window                      |
| Forced rebuilds run routinely                                | Keep them for suspected cache corruption                                          | Build logs show the cache restored                                  |
| Cloning takes a large share of build time                    | Remove large tracked files                                                        | The cloning time in build logs drops                                |
| Static generation renders thousands of rarely visited pages  | Prerender fewer pages                                                             | Fewer pages in static generation and a lower billable duration      |
| The build cache is near or over its limit                    | Shrink `node_modules` and the Turbopack cache                                     | The deployment summary shows the cache under its limit              |
| A project is pinned to Enhanced or Turbo                     | Switch it to Elastic                                                              | Lower CPU minutes per deployment with no new out-of-memory failures |
| The source map upload step is long                           | Upload fewer source maps                                                          | A shorter upload step in build logs                                 |

## Build less often

Every build you skip saves its full cost, so start with the fixes in this section. Most are settings changes that need no code.

### Skip unaffected projects in a monorepo

Vercel automatically skips builds for monorepo projects whose source code, internal dependencies, and lockfile dependencies haven't changed. Skipped builds don't count toward deployment limits or occupy concurrent build slots. New monorepo projects have skipping on by default. To check an existing project, open its **Settings**, go to **Build and Deployment**, and confirm that **Skip deployment** is enabled under **Root Directory**.

Automatic skipping works only when the repository meets all of these requirements:

- The repository is connected to GitHub.
  
- It uses npm, Yarn, pnpm, or Bun workspaces, and every package is listed in the workspace definition.
  
- Every package has a unique `name` in its `package.json`.
  
- Each package declares its dependencies on other packages in its own `package.json`.
  

If a project uses a custom **Ignored Build Step** and the repository meets these requirements, remove the [Ignored Build Step](https://vercel.com/docs/monorepos#skipping-unaffected-projects) and rely on automatic skipping.

### Use `turbo query affected` when automatic skipping doesn't fit

If your Turborepo doesn't meet the requirements above, set the project's **Ignored Build Step** to the command below:

```bash
turbo query affected --base=$VERCEL_GIT_PREVIOUS_SHA --packages your-package-name --exit-code
```

Replace `your-package-name` with the `name` field in the app's `package.json`, which can differ from the Vercel project name.

The command compares the current commit with the last successful deployment on the branch. It exits with `1` when the package or its dependencies changed, so Vercel builds, and with `0` when nothing changed, so Vercel cancels the build. If an earlier Turborepo import set the **Ignored Build Step** to `npx turbo-ignore`, replace it, because `turbo-ignore` is deprecated in favor of [`turbo query affected`](https://turborepo.dev/docs/reference/query#affected).

Three scenarios can still cost you builds:

- Vercel clones with `--depth=10`. If the last successful deployment is more than 10 commits back, Turborepo treats every package as changed, and the build runs.
  
- `VERCEL_GIT_PREVIOUS_SHA` is empty on a branch's first deployment, so test how the command behaves on a new branch before relying on it.
  
- Builds that the **Ignored Build Step** cancels still count toward deployment and concurrent build limits. Automatically skipped builds don't, so use automatic skipping when your repository qualifies.
  

### Turn off deployments for branches you never preview

Preview builds from branch pushes can make up most of a project's builds. To stop automatic deployments for branches that never need a preview, set [`git.deploymentEnabled`](https://vercel.com/docs/project-configuration/git-configuration#git.deploymentenabled) in `vercel.json`. This example turns off deployments for every branch whose name starts with `internal-`:

```json
{
  "git": {
    "deploymentEnabled": {
      "internal-*": false
    }
  }
}
```

Branches you don't list still deploy. Patterns use minimatch syntax, where `*` doesn’t match `/`, so `internal-*` doesn’t match a branch named `internal/fix-login`. Use `internal/**` for branch names with slashes, and test a new pattern on one branch first.

### Build previews only for pull requests

If branch pushes without a pull request don't need a preview, set this command as the project's **Ignored Build Step**:

```bash
[ "$VERCEL_TARGET_ENV" = "preview" ] && [ -z "$VERCEL_GIT_PULL_REQUEST_ID" ] && exit 0 || exit 1
```

The command cancels preview builds that have no pull request and lets every other build run, including production and custom environments. It checks `VERCEL_TARGET_ENV` because `VERCEL_ENV` is also `preview` for custom environments such as staging, which would cancel those builds too. Both variables require **Enable access to System Environment Variables** in the project's [environment variable settings](https://vercel.com/docs/environment-variables/system-environment-variables?__vercel_draft=1#enable-system-environment-variables).

Commits you push before opening the pull request don't build. Push a new commit after you open it to get a preview.

### Run one build per branch during commit bursts

When a bot or coding agent pushes many commits to one branch within minutes, the default on-demand concurrency mode, **Run all builds immediately**, builds and bills every commit in parallel.

To change it:

1. Open the project's **Settings**.
   
2. Go to **Build and Deployment**.
   
3. Set **On-Demand Concurrent Builds** to **Run up to one build per branch**.
   

Deployments to that branch then queue behind the active build. When it finishes, Vercel builds only the most recent queued deployment and skips the older ones, while other branches keep building in parallel. Learn more in [Build Queues](https://vercel.com/docs/builds/build-queues).

### Revalidate content instead of rebuilding

If a CMS webhook calls a Deploy Hook on every publish, each edit triggers a full build, and a bulk edit or content migration can queue dozens of builds. In a Next.js app, point the webhook at a Route Handler that revalidates only the content that changed:

```typescript
import type { NextRequest } from 'next/server'
import { revalidateTag } from 'next/cache'

export async function POST(request: NextRequest) {
  if (request.headers.get('x-revalidate-secret') !== process.env.REVALIDATE_SECRET) {
    return Response.json({ revalidated: false }, { status: 401 })
  }

  const { tag } = await request.json()
  revalidateTag(tag, 'max')

  return Response.json({ revalidated: true })
}
```

`revalidateTag(tag, 'max')` marks data tagged with `cacheTag` or with the `next.tags` option on `fetch` as stale. The next visitor gets the cached page while Next.js regenerates it in the background. If editors need the change on the next request, pass `{ expire: 0 }` instead of `'max'`. Use `revalidatePath` for content that isn't tagged. Learn more in the [`revalidateTag`](https://nextjs.org/docs/app/api-reference/functions/revalidateTag) [reference](https://nextjs.org/docs/app/api-reference/functions/revalidateTag).

### Fix recurring failures instead of redeploying

Vercel bills every redeploy of a failed build as a full build, whether you trigger it manually, from CI, or via other tooling. Redeploys also can't read Turborepo [Remote Cache artifacts](https://turborepo.dev/docs/core-concepts/remote-caching), so in a Turborepo, a redeploy reruns tasks that a normal build would restore from the cache. Find the recurring error across the failed deployments' build logs and fix it before you redeploy.

### Keep forced rebuilds for suspected cache corruption

The `vercel --force` flag, the `VERCEL_FORCE_NO_BUILD_CACHE=1` environment variable, and clearing **Use existing Build Cache** when you redeploy all skip the build cache. Redeploying without the build cache also skips the Turborepo and Nx Remote Cache, so every task starts from scratch. Use these only when you suspect a corrupted cache, and remove `VERCEL_FORCE_NO_BUILD_CACHE` once the issue is fixed. Learn more in [Troubleshooting Build Errors](https://vercel.com/docs/deployments/troubleshoot-a-build).

## Make each build shorter

Shorter builds use fewer CPU minutes. Build Diagnostics shows which phase takes longest, so start with that phase.

### Clone less

Vercel makes a shallow clone with `git clone --depth=10`, so clone time depends mostly on the files in the deployed commit rather than on older history. Remove generated files, build artifacts, archives, and large assets that the build doesn't need, commit the removal, and add generated paths to `.gitignore`. Setting a **Root Directory** or adding files to `.vercelignore` doesn't reduce what Vercel clones. To find unusually large files and folders with many files, run [git-sizer](https://github.com/github/git-sizer).

### Install fewer dependencies

In a monorepo, use a filtered install so each project installs only its own dependencies and its workspace dependencies. Set it as the project's **Install Command**, for example:

```json
{
  "installCommand": "pnpm install --filter web..."
}
```

[Using Monorepos](https://vercel.com/docs/monorepos#filtered-installs) lists the equivalent commands for Yarn, npm, and Bun. Skip dev dependencies only if the build doesn't use them. `npm install --omit=dev` shortens installs, but `next build` usually needs `devDependencies` such as TypeScript, Tailwind CSS, and PostCSS, so the flag breaks more Next.js builds than it helps. Test it in a preview before you adopt it.

### Do less work in the build command

For Next.js apps:

- **Prerender fewer pages.** Return a subset of paths from `generateStaticParams` so the rest render on first visit and Incremental Static Regeneration caches them. Preview builds can return an even smaller list when `process.env.VERCEL_ENV === 'preview'`. An empty array prerenders nothing at build time, but with Cache Components enabled an empty array fails the build, so return at least one path. Learn more in the [generateStaticParams reference](https://nextjs.org/docs/app/api-reference/functions/generate-static-params).
  
- **Build with Turbopack.** Turbopack has been the default bundler for `next build` since Next.js 16, and the release reported production builds 2 to 5 times faster than webpack. If your build command passes `--webpack`, test the build without it.
  
- **Avoid large inline strings on webpack.** If you still build with webpack, its cache warns that serializing large strings (NKiB) impacts deserialization performance when inline SVGs, base64 images, or large JSON blobs are embedded in code. Webpack serializes and deserializes that data on every build. Move it to `/public` and reference it, or store it as a Buffer and decode it when needed.
  
- **Remove build-time image processing.** `next/image` optimizes images when they're first requested, not during the build. If a build script or plugin preprocesses every image, removing it shortens the build, though that image work then counts toward Image Optimization usage.
  
- **Upload fewer source maps.** With Turbopack, Sentry's Next.js SDK uploads source maps after the build completes, which requires `@sentry/nextjs` 10.13.0 and Next.js 15.4.1 or later. That upload is billable build time. Leave `widenClientFileUpload` at its default of `false` unless you need `[native code]` stack frames resolved, and list files you don't debug in `sourcemaps.ignore`. If you don't need readable stack traces from preview deployments, set `sourcemaps.disable` for preview builds. Learn more in [Sentry's source maps docs](https://docs.sentry.io/platforms/javascript/guides/nextjs/sourcemaps/) and [build options](https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/build/#source-maps-options).
  

For any framework:

- **Use a current Node.js version.** Newer releases include runtime performance improvements. Node.js version is part of the build cache key, so the first build after an upgrade runs without a cache.
  
- **Bundle API routes into fewer functions.** Outside Next.js, each file in the `api/` directory deploys as its own Vercel Function, and Vercel builds each function separately. Route requests through one handler, for example with [Hono](https://vercel.com/docs/frameworks/backend/hono), so the routes share a function. Next.js already bundles routes this way.
  

## Cache what you can

A warm cache shortens installs and compiles, but a large cache adds upload time to every build. The goal is a cache that restores the work you'd otherwise repeat and stays under its limit.

### Know how Vercel's build cache works

Vercel caches dependencies and framework files between builds and keeps each cache for one month. The size limit depends on the build machine: 1.5 GB on Basic and Standard, 3 GB on Enhanced, and 4.75 GB on Turbo. Elastic uses the limit of the machine type assigned to each build. You can't choose which files Vercel caches. Learn more in [Build cache maximum size](https://vercel.com/docs/limits#build-cache-maximum-size).

The cache key combines your team, project, framework preset, root directory, Node.js version, package manager, and Git branch. Changing the preset, root directory, Node.js version, or package manager starts the next build with an empty cache. A new branch has no cache of its own, so its first build uses the cache from the latest production deployment.

Vercel uploads the cache after the build, and that upload counts toward billable build time, so a smaller cache costs less on every build. Filtered installs shrink `node_modules`, and the next section covers the Turbopack cache, which is often the largest item in a Next.js cache.

### Watch the Turbopack cache on Next.js 16.3 and later

Next.js 16.3 turns on Turbopack's persistent cache for `next build` by default and writes it to `.next/cache/turbopack`. Vercel restores `.next/cache` between builds, so later builds reuse earlier compile work. On large apps, the cache can outgrow the build cache limit. One report on a large app measured a 1.7 to 2.3 GB cache that took 115 to 150 seconds to create and upload on every build.

After you upgrade, check the cache size in the deployment summary. If it stays over the limit, or the upload takes longer than the warm compile saves, turn the cache off:

```typescript
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  experimental: {
    turbopackFileSystemCacheForBuild: false,
  },
}

export default nextConfig
```

Turning the option off stops Next.js from using the cache, but the folder already saved in Vercel's build cache is still restored and uploaded on each build. Redeploy once with **Use existing Build Cache** cleared so the next saved cache no longer includes it. Learn more in [Turbopack: What's New in Next.js 16.3](https://nextjs.org/blog/next-16-3-turbopack).

[Next.js 16.4](https://nextjs.org/blog/next-16-4) reduces Turbopack disk cache size. Upgrade and remeasure total billable duration before disabling persistent caching.

### Use Turborepo Remote Caching

[Remote Caching](https://vercel.com/docs/monorepos/remote-caching) shares Turborepo task outputs across your team, CI, and Vercel builds, and it requires no configuration during Vercel builds. It's free on every plan within fair-use limits. Monthly upload limits are 100 GB for Hobby, 1 TB for Pro, and 4 TB for Enterprise.

These behaviors affect your hit rate:

- Branch builds read artifacts that your default branch uploaded. After you connect a repository, run a build on the default branch so other branches get cache hits.
  
- Redeploys don't read or upload Remote Cache artifacts.
  
- Vercel expires artifacts after 7 days.
  

Exclude `.next/cache` from the build task's outputs in `turbo.json`. Vercel's build cache already restores that directory, so including it adds duplicate data to every artifact:

```json
{
  "tasks": {
    "build": {
      "outputs": [
        ".next/**", "!.next/cache/**"
      ]
    }
  }
}
```

Declare every environment variable that changes build output in `turbo.json`. Turborepo infers framework-inlined variables such as `NEXT_PUBLIC_*`, but other variables don't change the task hash unless you declare them. An undeclared variable can make Turborepo restore output built for another environment, such as staging config in a production deployment. Learn more in [Deploying Turborepo to Vercel](https://vercel.com/docs/monorepos/turborepo#handling-environment-variables).

A Next.js app that uses [Skew Protection](https://vercel.com/docs/skew-protection) always misses the Turborepo cache for its build task, because Skew Protection sets an environment variable that changes on every deployment. Other tasks in the repository can still hit the cache. If you use both, use Turborepo 2.4.1 or later to avoid missing assets in production. Learn more in the [limitations of Turborepo on Vercel](https://vercel.com/docs/monorepos/turborepo#limitations).

## Match the build machine to the workload

### Use Elastic for most projects

Elastic assigns each build a machine based on the project's recent build durations and memory use. CPU-bound and memory-bound builds get a larger machine, and builds that wouldn't use the extra vCPUs get a smaller one. If a build fails with an out-of-memory error, Vercel runs the next deployment on a larger machine, so an occasional out-of-memory failure isn't a reason to pin a machine.

To move a pinned project to Elastic, open the project's or team's **Settings**, go to **Build and Deployment**, then **Build Machines**, and select **Elastic**. Then compare billable duration and CPU minutes per deployment over the next several builds.

### Pin Enhanced or Turbo only when speed is worth the cost

Enhanced and Turbo shorten waits for CPU-heavy or memory-heavy builds, such as heavy bundling or expensive type checking. They rarely lower Build CPU Minutes because build time seldom decreases as quickly as vCPUs rise. Pin one only when shorter builds are worth paying more for.

To run a single urgent deployment on Turbo without changing the project's setting, deploy with `vc deploy --turbo`, or include `#VERCEL_BUILD_MACHINE=TURBO` in the commit message.

### Check Enterprise contracts and Standard billing

If your Enterprise contract enables Enhanced machines, builds use Enhanced by default. The **Build Machines** section of team settings shows whether this applies, and changing it requires your account manager.

On Pro, Vercel bills Standard builds only when on-demand concurrency or Elastic is enabled. Without either, Standard builds in the team's first concurrent build slot aren't billed, but builds can queue. Weigh queue time against cost before you change these settings.

## Keep deployment storage in check

Every build creates a deployment that Vercel stores until your retention policy removes it, so building less also lowers Deployment Storage. To reduce storage directly, set a [Deployment Retention Policy](https://vercel.com/docs/deployment-retention), which needs no code changes, and include shorter periods for canceled and errored deployments. Then shrink the output of the projects that still use the most storage. [Optimize Deployment Storage](https://vercel.com/docs/deployment-storage/optimize) walks through both steps.

Pausing projects through [Spend Management](https://vercel.com/docs/spend-management#pausing-projects) doesn't stop storage charges. Paused production deployments stop serving traffic, but Vercel keeps them, and Deployment Storage and Functions Storage stay billed until your retention policy or a manual deletion removes them.

## Confirm the savings

After each change, compare the same projects over a window of the same length as your baseline. Check both the number of builds in **Usage** and the CPU minutes per deployment on the deployment details page, because a fix can lower one without changing the other. The "How to confirm it worked" column in [Match findings to a fix](#match-findings-to-a-fix) lists the signal to look for after each change.

## Next steps

- [Managing Builds](https://vercel.com/docs/builds/managing-builds) for machine types, Elastic, and on-demand concurrency settings.
  
- [Pricing on Vercel](https://vercel.com/docs/pricing#builds) for how Vercel prices Build CPU Minutes.
  
- [Using Monorepos](https://vercel.com/docs/monorepos) for skipping unaffected projects and filtered installs.
  
- [Deploying Turborepo to Vercel](https://vercel.com/docs/monorepos/turborepo) for build commands, Remote Caching, Run Summary, and known limitations.
  
- [Optimize Deployment Storage](https://vercel.com/docs/deployment-storage/optimize) for retention policies and smaller deployments.

## Related Resources

- [How do I reduce my build time with Next.js on Vercel?](/kb/guide/how-do-i-reduce-my-build-time-with-next-js-on-vercel): Reduce Next.js build times on Vercel by pre-rendering fewer pages at build time, deferring generation with ISR and image optimization, and using faster build machines.