Configuring Remote Cache with Turborepo and Vercel
This guide walks through enabling Vercel Remote Cache for an existing Turborepo so that builds computed locally or on one CI runner are restored as cache hits everywhere else. For the hashing model and the artifact protocol underneath this setup, read Remote Caching and Distributed Build Coordination first — here we focus narrowly on the turbo login/turbo link handshake, supplying TURBO_TOKEN/TURBO_TEAM in headless CI, and proving with --summarize and --remote-only that hits are genuinely coming from the remote store rather than a warm local cache.
Prerequisites & Reproducible Setup
You need Turborepo 2.x in a workspace-enabled monorepo and a Vercel account (the cache is free on the hobby tier for the cache feature itself). Install and confirm the version:
# Turborepo 2.x. Run from the monorepo root.
pnpm add -Dw turbo@^2 # or: npm i -D turbo@^2 / yarn add -D turbo@^2
npx turbo --version # expect 2.x.y
# A minimal turbo.json must exist with cacheable tasks and outputs.
cat > turbo.json <<'JSON'
{
"$schema": "https://turborepo.com/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
}
}
}
JSON
Before involving the remote, confirm local caching is healthy — npx turbo run build twice should end with >>> FULL TURBO. Remote caching only shares what local caching already produces correctly.
Diagnosis Workflow: Linking and Verifying
- Authenticate the machine.
npx turbo loginopens a browser, authenticates against Vercel, and stores a token at~/.turbo/config.json. In a container without a browser, skip this and use the token method below. - Link the repository to a team’s cache.
npx turbo linkprompts for the Vercel scope (team) and writes the team id into.turbo/config.jsonin the repo. From now on everyturbo runreads and writes that team’s remote cache. - Force a remote round-trip. Run
npx turbo run build --remote-onlyto disable the local cache and prove the artifact moves through Vercel. The first run uploads (cache miss, executing); a second--remote-onlyrun on a freshly-cleared local cache should restore from remote. - Inspect what
turboactually did. Add-vvto surface HTTP traffic, or--summarizeto write a JSON run report and read each task’scache.source.
Complete Solution: Config, Token, and CI
For headless CI there is no interactive turbo login. Mint a Vercel access token (Account Settings → Tokens), store it and the team slug as CI secrets, and pass them as environment variables turbo reads automatically.
# .github/workflows/ci.yml — Turborepo 2.x, Node 20, pnpm.
name: CI
on:
pull_request:
push:
branches: [main]
env:
# turbo reads these to authenticate against Vercel Remote Cache headlessly.
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} # a Vercel access token
TURBO_TEAM: ${{ vars.TURBO_TEAM }} # your team slug, e.g. "team_acme"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # turbo needs git history to scope affected tasks
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
# --remote-only forces hits/misses through Vercel (no local cache in a
# clean runner anyway), and --summarize writes a report we assert on.
- run: pnpm exec turbo run build test lint --remote-only --summarize
# Fail the job if NOTHING was a remote hit on a no-op change (optional).
- name: Show cache sources
run: |
jq '.tasks[] | {task: .taskId, status: .cache.status, source: .cache.source}' \
.turbo/runs/*.json
To require that downloaded artifacts were produced by a trusted machine, enable signature verification. Vercel Remote Cache supports the x-artifact-tag HMAC header; turn it on in turbo.json and provide the shared key:
// turbo.json — add artifact signing. The key lives in env, never in the file.
{
"$schema": "https://turborepo.com/schema.json",
"remoteCache": { "signature": true },
"tasks": {
"build": { "dependsOn": ["^build"], "outputs": ["dist/**"] }
}
}
# Provide the HMAC key in every environment that reads or writes the cache.
# A downloaded artifact whose signature fails verification is rejected and
# the task is re-run locally instead of trusting tampered bytes.
export TURBO_REMOTE_CACHE_SIGNATURE_KEY="<32+ byte shared secret>"
Verification
Confirm remote hits with two commands. First, clear the local cache and run with --remote-only and --summarize:
# Turborepo 2.x. Prove a hit came from Vercel, not a warm local cache.
rm -rf node_modules/.cache/turbo
TURBO_TOKEN="$TURBO_TOKEN" TURBO_TEAM="$TURBO_TEAM" \
npx turbo run build --remote-only --summarize
# Read the cache source for every task from the generated run report.
jq '.tasks[] | {task: .taskId, status: .cache.status, source: .cache.source}' \
.turbo/runs/*.json
A successful remote restore shows "status": "HIT" with "source": "REMOTE" for each task, and the run finishes in seconds. The inline log also prints cache hit, replaying logs per task and a final >>> FULL TURBO. If you see "source": "LOCAL", your local cache was not actually cleared; if you see MISS, either the artifact was never uploaded or a hash input differs from the machine that seeded it.
Gotchas & Edge Cases
TURBO_TEAMmust be the slug, not the display name. Use the value from the team’s URL (team_acme), or the team id from.turbo/config.jsonafter a successfulturbo link. A wrong slug yields a silent fall-back to local-only with a 403 visible under-vv.- Don’t commit
.turbo/config.jsonsecrets.turbo linkwrites a team id (safe), but tokens belong in CI secrets and~/.turbo/config.json, never in the repo. Add.turbolog artifacts to.gitignore. fetch-depth: 0matters. Turborepo uses git to scope--filterand affected-package detection; a shallow checkout can over- or under-run tasks and muddy your hit-rate measurements.- Pass-through vs hashed env vars.
TURBO_TOKEN/TURBO_TEAM/CIshould sit inglobalPassThroughEnvso they authenticate without polluting the cache key; only output-affecting variables belong in a task’senv. Mixing these up is the most common cause of “works locally, misses in CI.”
Related
- Remote Caching and Distributed Build Coordination — the hashing model, artifact protocol, and self-hosted alternative behind this setup.
- esbuild & Turbopack Workflows — the overview tying task-level caching to incremental compilation.
- esbuild & Turbopack Version Compatibility Reference — which Turborepo and Next.js versions support the
tasksschema and cache flags used here.