Turborepo Remote Cache — CI Speed Without Leaking Secrets

Turborepo monorepo CI

Turborepo’s remote cache can turn a 20-minute monorepo CI into 4 minutes by reusing build / test outputs across machines. It can also serve a poisoned artifact or exfiltrate env-bound output if you treat the cache as a dumb blob store. Speed is the point; input hashing and secret hygiene are the price of admission.

What gets cached

Turbo hashes task inputs (files, env keys you declare, relevant lockfile bits) and stores outputs you list in turbo.json. A cache hit restores those outputs and skips the command. A hit with the wrong env still “succeeds” if you forgot to declare the env var as an input — that is how “works in CI, wrong API URL baked into the bundle” happens.

{
  "$schema": "https://turbo.build/schema.json",
  "remoteCache": { "signature": true },
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"],
      "env": ["NEXT_PUBLIC_API_URL"],
      "passThroughEnv": ["CI"]
    },
    "test": {
      "dependsOn": ["build"],
      "env": ["DATABASE_URL"]
    }
  }
}

If NEXT_PUBLIC_* affects the build but is missing from env, two CI jobs with different values share a bad hit.

Wire CI without putting tokens in logs

env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
# Vercel Remote Cache or self-hosted / Turborepo cache server

Rules:

  • Store TURBO_TOKEN in the CI secret store, not in the repo.
  • Restrict token scope to the team/org; rotate on staff offboarding.
  • Enable artifact signature when available so clients reject tampered payloads.
  • Do not echo turbo debug env in public forks PRs — fork PRs should get read-only cache or no remote cache.

Fork PR attack surface

Public repos: a malicious PR can try to poison the cache if write credentials are available to pull_request from forks. Prefer:

  • pull_request from forks → local cache only / read-only token
  • push to main / trusted branches → read-write token

GitHub’s pull_request_target mistakes are especially dangerous here — do not hand write tokens to untrusted code.

What not to put in outputs

OutputRisk
.env filesSecret leak via cache download
Files containing absolute machine paths + tokensInfo leak
Test DBs / dumpsPII
node_modules whole treeUsually wrong; huge and platform-specific

Keep outputs to build artifacts. Secrets belong in the environment at runtime, not in restored dist unless they are intentionally public (NEXT_PUBLIC_).

Debugging bad hits

turbo build --summarize
# or TURBO_LOG_ORDER=stream with dry run options per current CLI docs

Check the hash inputs: changed env? missing env key? unstable file in inputs (timestamps in generated files)? Pin generated code or exclude noise via inputs globs.

"build": {
  "inputs": ["src/**", "package.json", "tsconfig.json"],
  "outputs": ["dist/**"]
}

Self-hosted vs Vercel

Vercel Remote Cache is convenient if you already live there. Self-hosted S3-compatible caches need TLS, auth, and lifecycle policies. Same rules: signed artifacts, least-privilege tokens, no fork write access.

Local and CI sharing the same cache

Developers on the same remote cache as CI get huge wins — and must not upload outputs built with personal .env overrides. Document which env vars participate in the hash. If a teammate gets a cache hit that embeds someone else’s NEXT_PUBLIC_API_URL, your env list is incomplete. Treat that as a correctness bug. Keep fork PRs on read-only tokens (or no remote write) for public repos.

Remote cache is a force multiplier. Declare every build-affecting env var, lock down write tokens, and never cache secrets as outputs. Then the wall-clock win is earned instead of stolen from correctness.