---
title: Deploy to Vercel with Self-Hosted Git Pipelines (GitLab & Bitbucket)
description: Learn how to use GitLab Pipelines to deploy to Vercel including support for self-managed GitLab.
url: /kb/guide/how-can-i-use-gitlab-pipelines-with-vercel
canonical_url: "https://vercel.com/kb/guide/how-can-i-use-gitlab-pipelines-with-vercel"
published: 2025-11-03
last_updated: 2026-09-02
authors: Lee Robinson, Anna Z.
related:
  - /docs/git/vercel-for-gitlab
  - /docs/git/vercel-for-bitbucket
  - /docs/domains/working-with-domains/add-a-domain
  - /docs/cli
  - /docs/build-output-api
  - /kb/guide/how-do-i-use-a-vercel-api-access-token
  - /docs/cli/link
  - /docs/deploy-hooks
  - /docs/rest-api
  - /docs/deployments/deployment-policy
  - /docs/skew-protection
  - /docs/fluid-compute
  - /docs/deployments/environments
  - /docs/environment-variables/system-environment-variables
  - /kb/guide/branch-variables-and-domains-not-linked-to-cli-deployments
  - /docs/project-configuration/git-configuration
  - /docs/limits
  - /docs/cli/deploy
  - /docs/monorepos
  - /kb/guide/can-i-use-self-managed-gitlab-with-vercel
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

## How can I use GitLab and Bitbucket Pipelines with Vercel?

[Vercel for GitLab](https://vercel.com/docs/git/vercel-for-gitlab) and [Vercel for Bitbucket](https://vercel.com/docs/git/vercel-for-bitbucket) deploy your repository on every push and comment preview URLs on merge and pull requests. They also keep your [custom domains](https://vercel.com/docs/domains/working-with-domains/add-a-domain) pointed at the latest production deployment. For most teams, that connection is all the continuous integration and continuous delivery (CI/CD) they need.

Some teams need their own pipeline instead, either because they run tests and approval gates before a deploy or because their Git server runs inside their own network. GitLab CI/CD and Bitbucket Pipelines both handle that through the [Vercel CLI](https://vercel.com/docs/cli), and the setup is close to identical on either provider. Here's how to set up both, starting with the mechanism they share and ending with the failures that show up once your CI owns the build.

## When to replace the Vercel GitLab or Bitbucket integration with your own pipeline

Vercel's Git integration owns the deployment lifecycle. It builds each push, publishes a preview URL, promotes to production, and rolls back instantly. A pipeline adds the steps that run around that work. Three situations call for one:

- **Gated deploys:** Run your tests, security scans, and approval steps in the pipeline, then hand Vercel a build that has already passed them. `vercel build` and `vercel deploy` are separate commands, so the gate sits between them.
  
- **Self-hosted Git:** Vercel supports Self-Managed GitLab and Bitbucket Data Center through pipelines, which deploy from inside your own network using an access token.
  
- **Source code control:** `vercel build` compiles in your own runner and uploads only the output, so your source stays on your infrastructure.
  

If none of those apply, the built-in integration already covers the job, and adding a pipeline on top means managing build caching and preview URL comments yourself.

## How GitLab and Bitbucket Pipelines deploy to Vercel

The pipeline builds your project in its own runner, then uploads the finished output to Vercel instead of the source.

Three CLI commands do the work:

- `**vercel pull**`**:** Downloads the project settings and environment variables for the target environment into a local `.vercel` directory.
  
- `**vercel build**`**:** Detects your framework and compiles the project into a `.vercel/output` folder that follows the [Build Output API](https://vercel.com/docs/build-output-api) specification.
  
- `**vercel deploy --prebuilt**`**:** Uploads that output folder and creates a deployment. Vercel skips the build step entirely.
  

The `--prebuilt` flag tells Vercel the build is already done, so it serves the uploaded output as-is. Leave the flag off and Vercel rebuilds an artifact your pipeline already produced, which doubles your CI minutes and slows every deployment down.

Because the build no longer runs on Vercel, no Git metadata travels with the deployment, so both configs pass the branch details explicitly with the `--meta` option.

## How to set up GitLab CI/CD with Vercel

GitLab needs one pipeline file and three CI/CD variables. Start with the pipeline file, since the variables it references are the ones you'll add afterward.

### Write the .gitlab-ci.yml pipeline

Create a `.gitlab-ci.yml` file at the root of your repository with a preview job and a production job:

```yaml
default:
  image: node:24

stages:
  - deploy

deploy_preview:
  stage: deploy
  rules:
    - if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
  script:
    - npm install --global vercel@latest
    - vercel pull --yes --environment=preview --token=$VERCEL_TOKEN
    - vercel build --token=$VERCEL_TOKEN
    - vercel deploy --prebuilt --token=$VERCEL_TOKEN -m gitlabDeployment="1" -m gitlabCommitRef="$CI_COMMIT_BRANCH"

deploy_production:
  stage: deploy
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  script:
    - npm install --global vercel@latest
    - vercel pull --yes --environment=production --token=$VERCEL_TOKEN
    - vercel build --prod --token=$VERCEL_TOKEN
    - vercel deploy --prebuilt --prod --token=$VERCEL_TOKEN -m gitlabDeployment="1" -m gitlabCommitRef="$CI_COMMIT_BRANCH"
```

Set the `image` to the same Node.js major version your project builds with on Vercel, which supports 24.x, 22.x, and 20.x. The `rules` keyword replaces the older `only` and `except` keywords, which [GitLab has deprecated](https://docs.gitlab.com/ci/jobs/job_rules/) in favor of `rules`.

### Add the Vercel credentials as GitLab CI/CD variables

The pipeline reads three values that have to exist before it can run. Collect them in this order:

1. Create a [Vercel access token](https://vercel.com/kb/guide/how-do-i-use-a-vercel-api-access-token) and copy it.
   
2. Install the [Vercel CLI](https://vercel.com/docs/cli) locally and run `vercel login`.
   
3. Run [`vercel link`](https://vercel.com/docs/cli/link) in your project directory to create or connect a Vercel project.
   
4. Open the generated `.vercel/project.json` file and copy the `projectId` and `orgId` values.
   
5. In GitLab, add `VERCEL_TOKEN`, `VERCEL_ORG_ID`, and `VERCEL_PROJECT_ID` as [CI/CD variables](https://docs.gitlab.com/ci/variables/) under **Settings** > **CI/CD** > **Variables**.
   

Mark all three as masked and protected so they don't appear in job logs. Scope the token to a single team or project when you create it, which limits what a leaked CI credential can reach.

### Run the pipeline and confirm the deployment

Push a branch and open a merge request against your default branch. GitLab runs `deploy_preview`, and the pipeline log ends with the deployment URL, since `vercel deploy` always writes that URL to standard output.

Merging the request runs `deploy_production` and promotes the build to your production domains. Reverting and merging the revert produces a new production deployment from the earlier state, so rollbacks follow the same path as any other change.

## How to set up Bitbucket Pipelines with Vercel

Bitbucket uses the same three CLI commands and the same three credentials as GitLab, and `--prebuilt` behaves identically. Only the file syntax and the variable screen change.

### Write the bitbucket-pipelines.yml file

Create a `bitbucket-pipelines.yml` file at the root of your repository:

```yaml
image: node:24

pipelines:
  default:
    - step:
        name: Deploy preview to Vercel
        script:
          - npm install --global vercel@latest
          - vercel pull --yes --environment=preview --token=$VERCEL_TOKEN
          - vercel build --token=$VERCEL_TOKEN
          - vercel deploy --prebuilt --token=$VERCEL_TOKEN -m bitbucketDeployment="1" -m bitbucketCommitRef="$BITBUCKET_BRANCH"
  branches:
    main:
      - step:
          name: Deploy production to Vercel
          script:
            - npm install --global vercel@latest
            - vercel pull --yes --environment=production --token=$VERCEL_TOKEN
            - vercel build --prod --token=$VERCEL_TOKEN
            - vercel deploy --prebuilt --prod --token=$VERCEL_TOKEN -m bitbucketDeployment="1" -m bitbucketCommitRef="$BITBUCKET_BRANCH"
```

The `default` section runs for any branch not listed under `branches`, so every branch gets a preview deployment. If previews stop appearing, check for a narrower pattern like `feature/*`, which skips every branch that doesn't match.

### Add the Vercel credentials as Bitbucket repository variables

The pipeline needs three values stored as secured repository variables. Collect them in this order:

1. Create a [Vercel access token](https://vercel.com/kb/guide/how-do-i-use-a-vercel-api-access-token) and copy it.
   
2. Install the [Vercel CLI](https://vercel.com/docs/cli) locally and run `vercel login`.
   
3. Run [`vercel link`](https://vercel.com/docs/cli/link) in your project directory to create or connect a Vercel project.
   
4. Open the generated `.vercel/project.json` file and copy the `projectId` and `orgId` values.
   
5. In Bitbucket, add `VERCEL_TOKEN`, `VERCEL_ORG_ID`, and `VERCEL_PROJECT_ID` as [secured variables](https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/) under **Repository settings** > **Repository variables**.
   

Select **Secured** for the token so its value is masked in the build output. Repository variables are available to every branch, so scope the Vercel token to the team or project you deploy to.

### Run the pipeline and confirm the deployment

Push a branch and open a pull request. Bitbucket runs the `default` step, builds the project in its own runner, and uploads the output to Vercel as a preview deployment.

Merging the pull request into `main` runs the production step and promotes the build to your production domains. As with GitLab, reverting a merged commit and merging that revert creates a fresh production deployment from the earlier state.

## How to deploy from Bitbucket without the Vercel Bitbucket integration

You can deploy from Bitbucket with nothing installed on the Bitbucket side. The CLI authenticates with a token, so the Vercel for Bitbucket app is optional. This is the path for Bitbucket Data Center, for repositories, your security policy keeps off third-party apps, and for anyone who wants Bitbucket to own deployment triggering outright.

You use the same `bitbucket-pipelines.yml` file and the same three repository variables either way. Without the app installed, four behaviors change:

- **Preview URLs come from the pipeline log:** The Vercel bot posts preview URLs through the Git integration. Without it, read the URL from the pipeline log or post it with a Bitbucket step.
  
- **Branch metadata is passed explicitly:** Include `m bitbucketDeployment="1"` and `m bitbucketCommitRef` so the deployment binds to a branch rather than HEAD.
  
- **Redeploys run from the pipeline:** [Deploy Hooks](https://vercel.com/docs/deploy-hooks) work with a connected Git repository, so trigger redeploys from the pipeline or the [Vercel REST API](https://vercel.com/docs/rest-api) instead.
  
- **Skip conditions live in the pipeline:** The Ignored Build Step applies to builds that run on Vercel, so put the skip condition in `bitbucket-pipelines.yml` when your pipeline owns the build.
  

One team-level setting can block this path. [Deployment Policies](https://vercel.com/docs/deployments/deployment-policy) restrict which mechanisms may deploy to an environment, so confirm the CLI is permitted for the environment you're targeting before you debug the pipeline itself.

## Which Vercel features change in a GitLab or Bitbucket pipeline

Your pipeline produces the same artifact on the same infrastructure the Git integration would have used. Instant rollback, [Skew Protection](https://vercel.com/docs/skew-protection), and [Fluid compute](https://vercel.com/docs/fluid-compute) behave identically. The differences sit around the deployment rather than in it.

### Preview and production targeting

Preview and production are selected by two flags rather than by branch. `vercel pull --environment=preview` fetches the preview environment's variables, and adding `--prod` to `vercel build` and `vercel deploy` targets production. For a [custom environment](https://vercel.com/docs/deployments/environments), pass `--target=staging` on the deploy command using your environment's name.

### Environment variables at build time

Environment variables reach your build through `vercel pull`, which writes them into the local `.vercel` directory before `vercel build` runs. [System Environment Variables](https://vercel.com/docs/environment-variables/system-environment-variables) are the exception, since they're populated by Vercel's build infrastructure and your runner isn't it. Frameworks that read values such as `VERCEL_URL` or `VERCEL_ENV` at build time need those values set in the pipeline environment instead.

### Deploy hooks alongside a pipeline

Deploy hooks stay available whenever the Git integration is still connected. A [deploy hook](https://vercel.com/docs/deploy-hooks) is a URL that triggers a deployment of one branch on a GET or POST request, which suits content management system (CMS) publish events and scheduled rebuilds. It runs the build on Vercel rather than in your pipeline, so treat it as a second path into the same project rather than a replacement for the CLI.

## How to troubleshoot GitLab and Bitbucket Pipelines with Vercel

Most pipeline problems come from a short list of causes, and the symptoms are specific enough to identify quickly.

### Branch URLs and branch-scoped variables are missing

A deployment created without Git metadata binds to HEAD, so branch-specific domains and environment variables never match. Fix it by passing both metadata keys on the deploy command, since the [provider flag](https://vercel.com/kb/guide/branch-variables-and-domains-not-linked-to-cli-deployments) has to travel with the ref.

Pass the branch name on its own, not a full ref path. Values like `refs/heads/main`, or a code review ref such as `refs/changes/45/2345/1`, don't match a branch name, so the deployment falls back to HEAD and the branch URL never appears. Use `$CI_COMMIT_BRANCH` in GitLab and `$BITBUCKET_BRANCH` in Bitbucket, both of which hold the bare branch name.

### Your pipeline and the classic git integration both deploy the same commit

Search results and support threads use the phrase classic git integration to mean Vercel's built-in Git connection. If it's still connected when your pipeline starts deploying, both systems fire on the same push and you get two deployments per commit.

Turn off automatic deployments with [`git.deploymentEnabled`](https://vercel.com/docs/project-configuration/git-configuration) in `vercel.json`:

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

Setting it to an object instead of `false` turns deployments off per branch with minimatch patterns such as `"internal-*": false`. Keeping the integration connected while disabling its deployments preserves pull request comments and repository metadata.

### The build fails because system environment variables are missing

With `--prebuilt`, the build runs in your runner, so Vercel's System Environment Variables aren't available at build time. Frameworks that read them while compiling will fail or produce wrong output.

Set the values your framework needs directly in the pipeline environment, or move that project back to Git-based deployments. Skew Protection still works on Next.js under `--prebuilt` through a [custom deployment ID](https://vercel.com/docs/skew-protection), which cannot use the `dpl_` prefix.

### A GitLab merge request pipeline fails while the branch pipeline passes

GitLab can report a passing branch pipeline alongside a failing merge pipeline, which lets a merge request merge with failing tests. This is a [GitLab issue](https://vercel.com/docs/git/vercel-for-gitlab) rather than a Vercel behavior.

Deploying through the Vercel CLI, as both sample configs do, avoids the mismatch because a single pipeline owns both the build and the deployment. The deploy job either succeeds or fails on its own result, with no second pipeline reporting something different.

### The deployment hits the file upload limit

Projects with thousands of output files can exceed the [files limit](https://vercel.com/docs/limits) during upload. Compress the output before it ships by adding the archive option:

```bash
vercel deploy --prebuilt --prod --token=$VERCEL_TOKEN --archive=tgz
```

Archiving negates the source file caching that speeds up later uploads, so apply it only to the projects that need it. Working through these five causes in order accounts for the failures you're likeliest to hit before you need to open the build logs.

## Next steps

Once the pipeline runs cleanly on a branch, connect it to the rest of your workflow by adding your test and approval steps between `vercel build` and `vercel deploy`. Create the Vercel project the pipeline deploys to at [vercel.com/new](https://vercel.com/new), or start from the completed [GitLab CI/CD](https://github.com/vercel/examples/tree/main/ci-cd/gitlab-cicd) and [Bitbucket Pipelines](https://github.com/vercel/examples/tree/main/ci-cd/bitbucket-pipelines) examples.

## Related resources

- [Vercel for GitLab](https://vercel.com/docs/git/vercel-for-gitlab)
  
- [Vercel for Bitbucket](https://vercel.com/docs/git/vercel-for-bitbucket)
  
- [vercel deploy CLI reference](https://vercel.com/docs/cli/deploy)
  
- [Build Output API](https://vercel.com/docs/build-output-api)
  
- [Git configuration in vercel.json](https://vercel.com/docs/project-configuration/git-configuration)
  
- [Why are my branch specific variables and domains not linked to my CLI deployments?](https://vercel.com/kb/guide/branch-variables-and-domains-not-linked-to-cli-deployments)
  

## Frequently asked questions

### How do I deploy a monorepo to Vercel from GitLab or Bitbucket Pipelines?

Run `vercel link --repo` from the monorepo root to link every Vercel project at once, then scope each pipeline job with `vercel --cwd apps/your-app`. Each project needs its own `VERCEL_PROJECT_ID` variable. See the [monorepo documentation](https://vercel.com/docs/monorepos) for root directory and workspace settings.

### How do I stop Vercel from building commits that don't need a deployment?

When your pipeline builds the project, the skip condition belongs in your CI config: use `rules:changes` in GitLab or a condition on the step in Bitbucket. Vercel's Ignored Build Step applies to builds that run on Vercel, and a `--prebuilt` upload skips that build step entirely.

### Does Vercel support self-managed GitLab and Bitbucket Data Center?

Yes. Both are listed as supported products. Each deploys through a CLI pipeline that runs inside your own network and authenticates to Vercel with an access token, rather than through the built-in Git integration. See the [self-managed GitLab](https://vercel.com/kb/guide/can-i-use-self-managed-gitlab-with-vercel) guide for the short answer.

### Can I deploy to a Vercel custom environment from a pipeline?

Yes, pass `--target=` with your environment's name on the deploy command, for example `vercel deploy --prebuilt --target=staging`. Match it with `vercel pull --environment=staging` so the build receives that environment's variables instead of the preview set. Branch-scoped domains still need the `--meta` branch details. Custom environments are documented under [Environments](https://vercel.com/docs/deployments/environments).

### Do instant rollback and Skew Protection still work with --prebuilt deployments?

Instant rollback works unchanged, because it repoints the domain alias at an existing deployment with no rebuild. Skew Protection works on Next.js projects when you configure a custom deployment ID, which cannot use the `dpl_` prefix. Both behave the same as under the Git integration.