---
title: Migrate from GitHub Actions to Vercel CI
description: Move Turborepo tasks from GitHub Actions workflows to Vercel CI, migrate secrets to CI environment variables, and switch your required status checks.
url: "https://vercel.com/kb/guide/github-actions-to-vercel-ci"
published: 2026-10-29
last_updated: 2026-10-29
authors: Ben Sabic
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

Move Turborepo checks from GitHub Actions to Vercel CI with `ci` tags in `turbo.json`. Vercel CI selects affected tasks and dependencies from your task graph, reuses cached results, and runs uncached tasks in isolation.

Vercel Remote Cache is enabled automatically, and results appear in the GitHub merge box. After validating the migration, update your required status checks so failures block merges.

> Vercel CI is in Beta. Features and configuration may change.

## Prerequisites

Before you begin, make sure you have:

- Your GitHub repository connected to a Vercel project. The repository can connect to projects on only one Vercel team.
  
- Turborepo 2.11.7 or later in the repository. Earlier versions reject task-level `tags` with a `Found an unknown key` error.
  
- Vercel CLI 64.3.1 or later, installed locally
  
- Permission to edit branch protection rules or rulesets for the repository
  

Vercel CI currently runs tasks on Linux only and supports GitHub repositories. If any of your GitHub Actions jobs run on macOS or Windows runners, keep those jobs in GitHub Actions.

## How GitHub Actions concepts map to Vercel CI

Vercel CI replaces workflow YAML with your Turborepo configuration. Instead of describing jobs, runners, and steps in `.github/workflows/*.yml`, you tag the Turborepo tasks you want to run with `ci`, and Vercel CI uses the [Turborepo task graph](https://turborepo.dev/docs/core-concepts/package-and-task-graph#task-graph) to decide which `package#task` selectors your change affects. Vercel CI skips tasks that your change doesn't affect or that are already in Vercel Remote Cache.

| GitHub Actions                                            | Vercel CI                                                                               |
| --------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Workflow file in `.github/workflows/`                     | `ci` tags in `turbo.json`, plus an optional `vercel.json` for constants                 |
| Job or step that runs `turbo run test`                    | Turborepo task tagged `ci`, run with its dependencies                                   |
| `--filter`, `--affected`, or `paths` filters              | Affected-task selection from the task graph; review any additional filtering conditions |
| `runs-on` runner size                                     | 2 CPUs and 4 GB of memory per task by default, up to 32 CPUs with `ci:cpus:<count>`     |
| `TURBO_TOKEN` and `TURBO_TEAM` secrets for remote caching | Vercel Remote Cache, enabled with zero configuration                                    |
| Repository secrets                                        | Repository CI environment variables, with **Secret** selected                           |
| Organization secrets shared across repositories           | Shared Environment Variables linked to CI                                               |
| `env` values in workflow YAML                             | Constants in `vercel.json`                                                              |
| `.npmrc` setup step for private packages                  | An `NPM_RC` repository CI environment variable                                          |
| Required status check in the GitHub merge box             | Vercel CI check in the GitHub merge box                                                 |
| **Actions** tab logs and **Re-run jobs**                  | **CI** tab, Vercel CLI, Vercel MCP, and the REST API                                    |

Each Vercel CI task runs in its own isolated compute environment. GitHub Actions jobs often run several tasks on one runner sized for the heaviest task, so allocating compute per task can reduce over-provisioning for lighter tasks such as linting.

## Steps

### 1\. Audit your GitHub Actions workflows

Start by listing what each workflow in `.github/workflows/` does, so you know which jobs move to Vercel CI and which stay in GitHub Actions.

The following pnpm example assumes the root `package.json` declares its pnpm version in `packageManager`:

```yaml
name: CI

on:
  pull_request:
  push:
    branches: [main]

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    env:
      TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
      TURBO_TEAM: ${{ vars.TURBO_TEAM }}
      NODE_OPTIONS: --max-old-space-size=4096
    steps:
      - uses: actions/checkout@v7
        with:
          fetch-depth: 2
      - uses: pnpm/action-setup@v6
      - uses: actions/setup-node@v7
        with:
          node-version: 22
          cache: pnpm
      - run: pnpm install
      - run: pnpm turbo run lint typecheck test
```

For each workflow, record the following:

- **Tasks**: Every `turbo run` task the workflow runs, such as `lint`
  
- **Selection rules**: Branch triggers, path filters, and any conditions that limit the checks
  
- **Secrets and variables**: Every value under `secrets.*`, `vars.*`, and `env`
  
- **Runtime and setup**: Node.js and package-manager versions, runner image, and any setup commands the tasks require
  
- **Runner size**: Any job that uses a larger runner, which signals a task that needs more compute
  
- **Required checks**: The status check names your branch protection rules or rulesets require
  

Compare each task's runtime requirements with the [Vercel CI images](https://vercel.com/docs/ci/image) before migrating it. The workflow above uses Node.js 22 on Ubuntu. Vercel CI currently provides Node.js 24 and defaults to Amazon Linux 2023, so confirm that your tasks support that environment.

Use `ci:os:ubuntu` alongside `ci` for tasks that need Ubuntu. This changes the task image; dependency installation still runs on Amazon Linux.

Keep jobs that deploy, publish packages, run on a schedule, react to issue or label events, or need macOS or Windows runners in GitHub Actions. If a job runs a command that isn't a Turborepo task, add it as a `package.json` script and a task in `turbo.json` before you migrate it.

Migrate in two passes. Start with lint, type-check, formatter-check, and unit-test tasks that need no secrets, databases, browsers, or deployments, then move builds, integration tests, and end-to-end tests once the first pass runs cleanly.

### 2\. Enable Vercel CI for your repository

Vercel CI runs tasks only after you enable it for each Git repository. You can enable it with a coding agent or with the Vercel CLI.

To have a coding agent prepare your setup pull request, open the repository in your agent and send this prompt:

### Agent prompt

```txt
Set up Vercel CI for this repository. Run `npx skills use vercel/ci@setup` and follow the skill's instructions to select initial tasks, configure Vercel CI, and open a pull request.
```

Review the generated pull request against steps three to five, then continue with the comparison in step six.

To use the CLI, run this command from your local repository:

```bash
vercel ci enable
```

When the command succeeds, the CLI confirms the repository it enabled:

```text
✓ Enabled         Vercel CI
  Repository      your-org/your-repo
```

Running the command again is safe. For a repository that's already enabled, the CLI prints `Vercel CI is already enabled for your-org/your-repo.` Add `--json` for structured output.

With multiple Git remotes, the CLI prompts you to select one.

Pass `--remote <NAME>` to select a remote directly; this is required when multiple remotes exist and you use `--json` or `--non-interactive`. To enable Vercel CI for a different team, pass `--scope <team>`.

```bash
vercel ci enable --remote origin --scope your_team_slug_here
```

Enable Vercel CI before you push the branch with your configuration changes. Vercel CI starts a run when you push, so a push made before enabling doesn't produce a run.

### 3\. Tag the tasks that Vercel CI should run

Add the `ci` tag to the tasks selected for your first migration pass, preserving the dependencies they need:

```json
{
  "$schema": "https://turborepo.dev/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
    },
    "lint": {
      "tags": ["ci"]
    },
    "typecheck": {
      "dependsOn": ["^build"],
      "tags": ["ci"]
    },
    "test": {
      "dependsOn": ["build"],
      "tags": ["ci"]
    }
  }
}
```

In this `turbo.json`, `build` has no `ci` tag, so Vercel CI runs it only when a tagged task such as `test` depends on it. Add `"tags": ["ci"]` to `build` if you also want a build check for packages that no tagged task depends on.

In a later pass, use a [Package Configuration](https://turborepo.dev/docs/reference/package-configurations) to tag an end-to-end test for one package.

The root `turbo.json` doesn't need a `ci` tag for that task:

```json
{
  "$schema": "https://turborepo.dev/schema.json",
  "extends": ["//"],
  "tasks": {
    "e2e": {
      "dependsOn": ["build"],
      "tags": ["ci"]
    }
  }
}
```

Save this file as `apps/web/turbo.json`. If that package is named `web` in its `package.json`, its task selector is `web#e2e`. Finish configuring variables and compute before pushing the branch in step 6.

### 4\. Move secrets and environment variables

Vercel CI has three places for values that your GitHub Actions workflows read from `secrets`, `vars`, and `env`. Choose by scope and sensitivity:

| Value in GitHub Actions                                      | Where it goes in Vercel CI                                              |
| ------------------------------------------------------------ | ----------------------------------------------------------------------- |
| Repository secret                                            | Repository CI environment variable, with **Secret** selected            |
| Repository variable                                          | Repository CI environment variable                                      |
| Organization secret or variable used by several repositories | Shared Environment Variable, linked to each repository                  |
| Non-secret `env` value in the workflow file                  | Constant in `vercel.json`                                               |
| `TURBO_TOKEN` and `TURBO_TEAM`                               | Nothing. Vercel CI enables Vercel Remote Cache with zero configuration. |

To add a repository CI environment variable:

1. Open [CI environment variables](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fsettings%2Fenvironment-variables%3Fview%3Dci) in the Vercel dashboard.
   
2. Select the repository that should receive the variable.
   
3. Select **Add Variable**.
   
4. Enter the key and value. Select **Secret** for sensitive values, which makes them write-only.
   
5. Select **Add Variable** to save.
   

Vercel CI makes the variable available to later runs for that repository.

To reuse a value across repositories, create or edit a Shared Environment Variable from the **Shared** tab. Include the **Preview** environment, then select each repository under **Link to CI (Optional)**.

If your workflow wrote an `.npmrc` file to install private packages, add a repository CI environment variable named `NPM_RC` with **Secret** selected. Enter your complete npm configuration as the value:

```text
//registry.npmjs.org/:_authToken=your_access_token_here
```

Vercel CI writes `NPM_RC` to `$HOME/.npmrc` while installing dependencies, then removes the file. For a custom registry, change the registry hostname and add any scope configuration your packages need.

Move non-secret values from the workflow's `env` block into the `ci` object in your repository's root `vercel.json`. Merge the following configuration with any existing settings. Use `ci.bootstrapping.installEnv` for dependency installation and `ci.tasks` for a task name or a `package#task` selector:

```json
{
  "ci": {
    "bootstrapping": {
      "installEnv": {
        "NODE_OPTIONS": {
          "constant": {
            "value": "--max-old-space-size=4096"
          }
        }
      }
    },
    "tasks": [
      {
        "name": "web#test",
        "env": {
          "TEST_RETRIES": {
            "constant": {
              "value": "2"
            }
          }
        }
      }
    ]
  }
}
```

Here, `NODE_OPTIONS` applies only during dependency installation. Add it to task-level `env` entries too if the migrated tasks need the same setting.

Constants in `vercel.json` override linked Shared Environment Variables with the same key. Don't commit secrets to `vercel.json`, because anyone with access to the repository can read it.

Declare variables that tasks read in `turbo.json` so Turborepo's strict environment mode passes them through. Add `TEST_RETRIES` to the existing `test` task without removing its dependencies or tags:

```json
{
  "$schema": "https://turborepo.dev/schema.json",
  "tasks": {
    "test": {
      "dependsOn": ["build"],
      "env": ["TEST_RETRIES"],
      "tags": ["ci"]
    }
  }
}
```

Values in `env` affect the task's cache hash. Use `passThroughEnv` for values that the task needs but that should not affect caching. Variables used only during dependency installation do not need a task declaration.

### 5\. Size compute for heavy tasks

Vercel CI assigns 2 CPUs and 4 GB of memory to each task by default. If a job in step 1 needed a larger GitHub Actions runner, give the matching task more CPUs with a `ci:cpus:<count>` tag, up to 32 CPUs:

```json
{
  "$schema": "https://turborepo.dev/schema.json",
  "tasks": {
    "test": {
      "dependsOn": ["build"],
      "env": ["TEST_RETRIES"],
      "tags": ["ci", "ci:cpus:8"]
    }
  }
}
```

Vercel CI assigns 2 GB of memory per CPU:

| CPUs | Memory |
| ---- | ------ |
| 1    | 2 GB   |
| 2    | 4 GB   |
| 4    | 8 GB   |
| 8    | 16 GB  |
| 16   | 32 GB  |
| 32   | 64 GB  |

Set a CPU tag in a package's `turbo.json` to allocate compute for that package's task independently of the root setting.

Package-level `tags` replace the inherited array by default. Keep `ci` in the replacement array when changing CPU allocation so the task remains selected for CI. Tasks without `ci` can still run as dependencies:

```json
{
  "$schema": "https://turborepo.dev/schema.json",
  "extends": ["//"],
  "tasks": {
    "test": {
      "tags": ["ci", "ci:cpus:16"]
    }
  }
}
```

### 6\. Run both pipelines side by side

With your GitHub Actions workflow still running, commit the changes from steps 3 to 5 on a branch, push it, and open a pull request. Compare both pipelines in the GitHub merge box before switching over.

The `pull_request` workflow above tests GitHub's merge commit by default. Vercel CI runs on the pushed commit, so account for any changes introduced by the merge when comparing task results.

For each pull request, confirm the following:

- Vercel CI selected the expected affected tasks and dependencies, with successful execution or valid cached results
  
- Corresponding tasks pass in both pipelines, with differences between the tested revisions accounted for
  
- Any failures caused by missing variables, runtime differences, or setup steps have been resolved
  

Open the run from the link in the GitHub merge box, or find it in the [CI tab](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fci&title=) for your team.

Run these commands from a checkout whose `origin` points to the enabled repository, with the owning Vercel team selected. Add `--scope your_team_slug_here` to each command if you need to select that team explicitly. Without a run ID, `inspect` and `logs` use the current branch and commit:

```bash
# Wait up to 10 minutes for an existing run to finish, exiting 1 if it fails
vercel ci inspect --wait

# Show failed task logs
vercel ci logs --failed

# List failed runs across every branch
vercel ci list --all-branches --status failed
```

If `inspect` reports no run immediately after a push, retry once the run appears in the dashboard. The `--wait` flag waits for an existing run to finish.

To inspect runs from your coding agent, connect [Vercel MCP](https://vercel.com/docs/agent-resources/vercel-mcp) and use its tools:

| Tool                            | Use it to                                                             |
| ------------------------------- | --------------------------------------------------------------------- |
| `list_vercel_ci_invocations`    | Find runs by `repository`, `branchName`, `status`, or `conclusion`    |
| `get_vercel_ci_invocation_tree` | See every job and task in a run, defaulting to the latest attempt     |
| `get_vercel_ci_task_logs`       | Read logs grouped by task, filtered with `conclusion` set to `failed` |
| `retry_vercel_ci_invocation`    | Start another attempt of a finished run                               |

To inspect a pull request's Vercel CI results, send this prompt:

### Agent prompt

```txt
Use Vercel MCP to find the latest Vercel CI invocation for the my-org/my-repo repository on the my-feature-branch branch. Show me which tasks ran, which were cached or skipped, and the error logs for any failed tasks.
```

After `retry_vercel_ci_invocation` starts a new attempt, check the new attempt's status and logs before treating the run as passed.

### 7\. Update your required status checks

Once Vercel CI results match GitHub Actions across several pull requests, make Vercel CI the check that blocks merges.

In your repository's [branch protection rules](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/managing-a-branch-protection-rule) or rulesets:

1. Add the Vercel CI check as a required status check.
   
2. Remove the GitHub Actions checks for the jobs that Vercel CI now covers.
   
3. Save the rule.
   

Vercel CI failures now block merges the same way a failed GitHub Actions check does.

### 8\. Remove the migrated workflows

Delete the GitHub Actions jobs, or whole workflow files, that Vercel CI now runs. Keep the workflows you set aside in step 1, such as deployments, releases, and scheduled jobs.

After removing the jobs, delete the `TURBO_TOKEN` and `TURBO_TEAM` secrets and variables from your GitHub repository if no remaining workflow uses them.

## Troubleshooting

### Why a task didn't run in Vercel CI

Vercel CI selects tasks tagged `ci` and their dependencies, then reuses cached results and skips unaffected work. Check that each task name in `turbo.json` matches its `package.json` script.

If an expected task is missing, check package-level tag overrides and any `!ci` exclusion. Inspect the run's task tree for statuses and dependency failures before checking logs for a task that ran.

### Why a task can't read an environment variable

Vercel CI makes new repository variables available to later runs. Push a new commit after adding them.

Shared Environment Variables must include **Preview** and be linked under **Link to CI (Optional)**. Check for a constant with the same key in `vercel.json`, which takes precedence over the shared value.

If a task still receives `undefined`, check its `env` and `passThroughEnv` declarations in `turbo.json`, including package-level overrides. The task-variable setup in step 4 shows where to add the declaration.

### Why installing private packages fails

Private npm packages need a repository CI environment variable named `NPM_RC` that contains your full npm configuration. For a custom registry or scoped packages, update the registry hostname in `NPM_RC` and add the scope configuration, the same way you would in a local `.npmrc` file.

### Why a task runs out of memory

Vercel CI gives each task 4 GB of memory by default. Add a `ci:cpus:<count>` tag to the task, such as `ci:cpus:4` for 8 GB, since memory scales at 2 GB per CPU.

### Why `vercel ci enable` fails

Confirm you're running Vercel CLI 64.3.1 or later and Turborepo 2.11.7 or later. The repository must be connected to Vercel projects on only one team. In non-interactive mode, a repository with more than one Git remote requires `--remote <NAME>`.

The CLI error tells you which case you hit:

| Error message                                                           | Fix                                                                                                   |
| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `Couldn't find your-org/your-repo connected to a project on this team.` | Connect the repository to a project with `vercel git connect`, or pass the owning team with `--scope` |
| `It's connected to projects on more than one team.`                     | Disconnect the repository from projects on every team except one                                      |
| `You don't have permission to change Vercel CI settings for this team.` | Ask a team member with permission to run the command                                                  |

### Why turbo reports `Found an unknown key tags`

Task-level `tags` require a newer Turborepo release. Upgrade the `turbo` dependency to 2.11.7 or later with your package manager, which regenerates the lockfile, then push again.

## Next steps

- Read the [Vercel CI documentation](https://vercel.com/docs/ci) for the full configuration reference and current limitations.
  
- Manage runs from your terminal with the [`vercel ci`](https://vercel.com/docs/cli/ci) [CLI command](https://vercel.com/docs/cli/ci).
  
- Connect [Vercel MCP](https://vercel.com/docs/agent-resources/vercel-mcp) so your coding agent can inspect, debug, and retry Vercel CI runs.
  
- Learn how [Vercel Remote Cache](https://vercel.com/docs/monorepos/remote-caching#vercel-remote-cache) shares task results across runs and machines.
  
- Review the [Turborepo task graph](https://turborepo.dev/docs/core-concepts/package-and-task-graph#task-graph) and [Package Configurations](https://turborepo.dev/docs/reference/package-configurations) to fine-tune which tasks Vercel CI runs.

## Related Documentation

- [Vercel CI](https://vercel.com/docs/ci)