ADR 006: Turborepo for Monorepo Orchestration

Status: Accepted Date: 2025-03-15

Context

Our monorepo (platform-core) contains 7+ packages (design system, shared types, utils, configs). We need a tool to orchestrate build/test/lint tasks across packages with proper dependency ordering, caching, and fast CI.

Decision

Use Turborepo (turbo@^2.8.10) for task orchestration and caching in the monorepo.

Turborepo Turborepo Positive Simple turbo.json config Content-addressable cache Remote caching Incremental builds Negative Less features than Nx Cache tied to Vercel No polyrepo support

Consequences

Positive

  • Simple configuration: single turbo.json defines task pipeline
  • Content-addressable caching: skips tasks when inputs haven't changed
  • Remote caching (Vercel or self-hosted): CI cache shared across developers and CI runs
  • Understands package dependency graph: runs tasks in correct topological order
  • Incremental builds: only rebuilds changed packages and their dependents
  • Fast: written in Rust, minimal overhead
  • --filter flag for targeting specific packages
  • Integrates seamlessly with pnpm workspaces
  • Low learning curve for developers

Negative

  • Less feature-rich than Nx (no code generation, no dependency graph visualization, no migration tools)
  • Remote caching tied to Vercel account (or self-host)
  • No built-in support for polyrepo management (only manages the monorepo)
  • Limited plugin ecosystem compared to Nx
  • No affected/changed detection for PRs (relies on manual --filter or --since)

Remote Cache Configuration

Cache Provider

Turborepo remote caching is configured using the Vercel Remote Cache as the primary provider. Self-hosted alternatives (e.g., turborepo-remote-cache on Docker, Ducktape) are available if Vercel hosting is not desired, but the Vercel-managed option is recommended for simplicity and reliability.

  • Vercel Remote Cache: Enabled by linking the repository to a Vercel team via turbo login and turbo link. Once linked, cache artifacts are automatically uploaded and downloaded from Vercel's CDN.
  • Self-hosted option: For organizations that require data residency or on-premise control, turborepo-remote-cache can be deployed as a standalone HTTP server backed by S3-compatible storage. The TURBO_API, TURBO_TEAM, and TURBO_TOKEN environment variables configure the custom endpoint.

Cache Signing

To prevent cache poisoning and ensure artifact integrity, cache signing is enabled:

  • Set TURBO_REMOTE_CACHE_SIGNATURE_KEY to a shared secret (minimum 32 characters) in CI and local environments.
  • When enabled, Turborepo signs each artifact before upload and verifies the signature on download. Any tampered artifact is rejected and the task re-executes.
  • Rotate the signing key periodically (recommended: every 90 days) and update it in all CI pipelines and developer environments simultaneously.

Team Sharing Configuration

  • All developers on the team authenticate via turbo login using their Vercel account (linked to the team).
  • The TURBO_TEAM environment variable (or --team flag) ensures artifacts are scoped to the correct team namespace, preventing cross-team cache collisions.
  • CI pipelines use a service token (TURBO_TOKEN) with write access to push cache artifacts. Developer machines have read-write access by default after linking.
  • Cache TTL: Vercel remote cache retains artifacts for 7 days by default. Older artifacts are evicted automatically, keeping storage usage predictable.

Alternatives Considered

Nx

More feature-rich (code generators, dependency graph UI, affected command, plugins for many frameworks). But: heavier setup, steeper learning curve, generates more config files. The nx.json + project.json per package approach is more complex. Overkill for a library-only monorepo with 7 packages. Better suited for monorepos that also contain applications.

Lerna

Legacy tool, now maintained by Nx. Lerna alone is a publishing tool, not a task runner. Using Lerna + Nx is effectively just using Nx. No caching without Nx.

npm/pnpm scripts only

No caching, no dependency-aware task execution, no parallelism optimization. Fine for <3 packages, breaks down at scale.

Bazel

Extremely powerful build system with fine-grained caching. But: massive learning curve, complex setup, not designed for JavaScript-first workflows. Overkill for this project size.

Moon

Newer Rust-based monorepo tool. Promising but less mature, smaller community, less proven in production.