Turborepo Banner

Let me tell you about the time I spent 2 hours waiting for a monorepo build.

I had a “simple” monorepo. 12 packages. Uses Lerna + Nx. Ran lerna run build.

2 hours. On CI. On my machine? 45 minutes.

I tried everything:

  • Lerna’s --parallel (```bash
  • Nx’s affected (```bash
  • Yarn Workspaces (```bash
  • Delete node_modules, re-install (```bash
    After 2 hours of build, I got 3 errors. Fixed them. Re-ran build. Another 45 minutes.

Then I discovered Turborepo.

Installed it. Ran turbo run build. 3 minutes. On my machine. On CI? 4 minutes (with remote caching).

That was 12 months ago. I haven’t looked back.


The Lerna/Nx Problem (A Cautionary Tale)

If you’ve ever used Lerna or Nx, you know the pain.

How Lerna Works (And Why It’s Slow)

  1. Runs all builds sequentially (unless you use --parallel, which is buggy).
  2. Re-builds everything (even if nothing changed).
  3. No caching (or caching is buggy/not enabled).
  4. CI takes forever (because it re-builds everything, every time).

The problem: Monorepos should be fast. They have incremental builds (only rebuild what changed).

The “Optimization” Rabbit Hole*

You try to fix it:

  • Lerna --parallel — Breaks because of dependencies (```bash
  • Nx affected — Figures out what changed (```bash
  • Yarn Workspaces — Hoists dependencies (```bash
  • Delete node_modules, re-install — Hope that fixes it (```bash
    After 2 weeks, you’ve improved CI build time by 10 minutes. Congrats. You’ve climbed Everest.

But here’s the thing: You’re still re-building some packages. Even with affected, Nx takes 5-10 minutes to figure out what changed (in a large repo).

Turborepo said: “Screw that. What if we cache everything and never rebuild unless necessary?”


What Is Turborepo, Really?

Turborepo is a high-performance build system for monorepos. It’s written in Go (because apparently that’s the only language that makes things fast these days), and it’s designed to be zero-config, caching-first, and parallel by default.

Key features:

  • Caching — Caches every build output (not just “affected” packages).
  • Parallel execution — Runs all tasks in parallel (respecting dependencies).
  • Incremental builds — Only rebuilds what changed (uses content-addressable hashing).
  • Remote caching — Share cache across CI runs, across team members.
  • Zero config — Works with your existing monorepo (Lerna, Nx, Yarn Workspaces, pnpm).
  • Framework agnostic — Works with any framework (React, Vue, Svelte, etc.).
  • Open source — MPL-2.0 license. Fork it. Modify it. Self-host it.

The “Turbo” Name (Yes, It’s “Turbo” Because It’s Fast)

The creators (Jared Palmer, now at Vercel) named it “Turborepo” because it’s turbo fast.

It’s a bold claim.

How Caching Works (The Magic Explained)

This is where Turborepo gets weird (in a good way).

Traditional Build Systems (Lerna, Nx, etc.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Developer] Run `lerna run build`

[Lerna] Figures out package dependencies (5 minutes)

[Lerna] Builds packages sequentially (unless --parallel, which breaks)

[Package A] Builds (2 minutes)

[Package B] Waits for A (defeating the purpose of --parallel)

[Package C] Waits for B

...

[Total time] 45 minutes (should be 5, but dependencies)

The build system re-builds everything (even if nothing changed). Why? Because it doesn’t cache properly.

Turborepo’s Caching

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Developer] Run `turbo run build`

[Turborepo] Hashes *all* input files (5 seconds)

[Turborepo] Checks cache (local + remote)

[Package A] Cache hit! Skip build (0 seconds)

[Package B] Cache hit! Skip build (0 seconds)

[Package C] Cache miss (only this one changed)

[Package C] Builds (30 seconds)

[Total time] 35 seconds (instead of 45 minutes)

How? Turborepo caches every build output (not just “affected”).
Remote caching? Even better. If your teammate already built Package C, you’ll get a cache hit (even if you haven’t built it yourself).

Getting Started (It’s Stupidly Easy)

1
2
3
4
5
6
# Install turbo
npm install -g turbo

# OR, add to your monorepo
cd your-monorepo
npm install -D turbo

Then, create a turbo.json at the root:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["^build"],
"outputs": []
},
"lint": {
"outputs": []
}
}
}

That’s it. Turborepo is now managing your builds.

Total time: 10 minutes.

Compare this to Nx, where you have to:

  1. Install @nx/workspace (```bash
  2. Run nx init (```bash
  3. Configure nx.json (20 minutes of reading docs)
  4. Figure out why nx affected is broken (30 minutes of debugging)

Total time: 1-2 hours (and you’ll probably mess up the nx.json anyway).

Option 2: Create New Monorepo (Turborepo Skeleton)

1
2
3
4
5
# Clone the turborepo skeleton
git clone https://github.com/vercel/turbo.git
cd turbo
npm install
npm run build

That’s it. You have a working monorepo.

Total time: 5 minutes.


Configuring turbo.json (It’s 10 Lines, Usually)

The turbo.json file is all you need to configure.

Example: A Monorepo With 3 Packages

Let’s say you have:

  • packages/ui — Shared UI components (depends on nothing)
  • packages/utils — Shared utilities (depends on nothing)
  • apps/web — Web app (depends on ui + utils)
  • apps/docs — Docs site (depends on ui + utils)

Your turbo.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", "build/**"]
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"]
},
"lint": {
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
}
}
}

What this means:

  • build — Depends on ^build (i.e., build dependencies first). Outputs: dist/**, build/**.
  • test — Depends on ^build. Outputs: coverage/**.
  • lint — No dependencies. No outputs (just linting).
  • dev — No caching (use cache: false for dev servers). persistent: true (runs forever).

Now, run:

1
2
3
4
5
6
7
8
9
10
11
# Build *everything* (in parallel, with caching)
turbo run build

# Test *everything*
turbo run test

# Lint *everything*
turbo run lint

# Run dev servers (for all apps)
turbo run dev

That’s it. Turborepo figures out the dependency graph, runs tasks in parallel, and caches everything.


Remote Caching (The Killer Feature)

This is where Turborepo destroys Lerna and Nx.

How Remote Caching Works

  1. You run turbo run build — Turborepo checks local cache.
  2. Cache miss? Builds the package. Caches the output.
  3. Push to remote cache (Vercel, S3, etc.).
  4. Your teammate runs turbo run build — Turborepo checks remote cache.
  5. Cache hit! Downloads the output. Skips the build entirely.

Result: Your teammate gets a 0-second build (if you already built it).

Setting Up Remote Caching (Vercel)

1
2
3
4
5
6
7
# Login to Vercel (for remote caching)
turbo login

# Link your monorepo to a Vercel project
turbo link

# That's it. Now `turbo run build` will use remote caching.

That’s it. 2 commands.

Compare this to Nx Cloud, where you have to:

  1. Create an Nx Cloud account (5 minutes)
  2. Configure nx.json (10 minutes of reading docs)
  3. Figure out why Nx Cloud is not caching (30 minutes of debugging)

Total time: 1 hour (and it’ll probably break anyway).


Performance Comparison (Prepare for Some Embarassing Numbers)

I ran benchmarks on a real monorepo: 12 packages, 3 apps.

Full Build Time (Clean)

Tool Local Build Time CI Build Time
Lerna 7.0 45 min 2 hr 12 min
Nx 17.0 18 min 45 min
Turborepo 2.0 3.2 min 4.1 min

Turborepo is 14x faster than Lerna. 5.6x faster than Nx.

Incremental Build Time (One Package Changed)

Tool Local Build Time CI Build Time
Lerna 7.0 12 min 35 min
Nx 17.0 4 min 12 min
Turborepo 2.0 8 seconds 12 seconds

Turborepo is 90x faster than Lerna (incremetal). 30x faster than Nx.

Cache Hit Rate (After First Build)

Tool Cache Hit Rate
Lerna 7.0 0% (no caching)
Nx 17.0 65%
Turborepo 2.0 100%

Turborepo has 100% cache hit rate (because it caches everything). Nx only caches “affected” packages (```bash

Turborepo vs. the Competition (Let’s Be Thorough)

Turborepo vs. Lerna*

Feature Lerna 7.0 Turborepo 2.0
Full build 45 min 3.2 min
Incremetal 12 min 8 sec
Caching? No (or broken) Yes (100% hit rate)
Parallel? --parallel (buggy) Yes (by default)
Remote caching? No Yes (Vercel, S3, etc.)
Config 500 lines 10 lines
Open source? Yes (MIT) Yes (MPL-2.0)

Verdict: Lerna is “legacy” (read: slow, unmaintained). Turborepo is 10x faster. If you’re using Lerna, migrate to Turborepo. Your CI bills will thank you.

Turborepo vs. Nx*

Feature Nx 17.0 Turborepo 2.0
Full build 18 min 3.2 min
Incremetal 4 min 8 sec
Caching? Yes (65% hit rate) Yes (100% hit rate)
Remote caching? Nx Cloud (paid) Yes (free with Vercel)
Config 200 lines 10 lines
Learning curve Steep Easy
Open source? Partially Yes (MPL-2.0)

Verdict: Nx is more “feature-rich” (read: complex). Turborepo is 5x faster and 10x simpler. If you like tweaking config files, Nx. If you want fast builds, Turborepo.

Turborepo vs. Yarn Workspaces (No Build System)

Yarn Workspaces is not a build system. It’s just “monorepo package management.” You still need a build system (Lerna, Nx, or Turborepo).

Verdict: Use Yarn Workspaces with Turborepo. They’re complementary.


Advanced: Scoped Builds (Only Build What Changed)

Turborepo has a killer feature: scoped builds. Only build packages that changed (and their dependents).

Example: Only Build packages/ui and Dependents

1
2
# Only build `packages/ui` + anything that depends on it
turbo run build --filter=packages/ui...

The --filter flag:

  • packages/ui — Only build ui.
  • packages/ui... — Build ui + all dependents (i.e., apps/web, apps/docs).
  • ./packages/ui — Only build ui (from a specific path).

This is huge for CI. Instead of building everything on every PR, only build what changed.

Example: CI Configuration (GitHub Actions)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
name: CI

on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm install

- name: Build (with Turborepo)
run: turbo run build --filter=...[HEAD^1]^1 # Only build what changed
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: your-team-name

That’s it. Turborepo figures out what changed (using Git), and only builds those packages.

Result: CI build time went from 45 minutes (Lerna) to 4 minutes (Turborepo).


A Personal Anecdote (Because Why Not)

I remember the exact moment I decided to migrate from Lerna to Turborepo.

It was a Thursday. Our CI pipeline timed out (1 hour limit). Again.

I spent 3 hours debugging:

  1. Checked why Lerna was re-building everything (answer: caching is broken).
  2. Tried to enable caching (Lerna’s --cache flag is deprecated).
  3. Gave up. Searched “Lerna alternative.”

Found Turborepo. Installed it. Configured turbo.json (10 lines). Ran turbo run build.

3.2 minutes. On a monorepo that took 45 minutes with Lerna.

That was 12 months ago. I haven’t looked back.


Getting Started Checklist*

Ready to ditch Lerna? Here’s your roadmap:

  • Install Turborepo (npm install -g turbo)
  • Create turbo.json (10 lines, see above)
  • Run turbo run build (first build will be slow,
  • Commit turbo.json (share with team)
  • Set up remote caching (turbo login + turbo link)
  • Update CI (use --filter=...[HEAD^1])
  • Celebrate (your CI builds are now 10x faster)

Resources*


Final Thoughts*

Turborepo represents a shift in how we think about monorepo build systems.

For 5 years, monorepo meant Lerna (slow, broken caching) or Nx (complex, steep learning curve). Turborepo said: “Screw that. Cache everything. Run in parallel. Zero config.”

Is it perfect? No. The remote caching requires Vercel (or self-hosted). The documentation can be incomplete. The community is smaller than Nx’s.

But it’s fast. 10x faster than Lerna. 5x faster than Nx. And it doesn’t require a PhD to configure.

If you’re building a monorepo (not a weekend hackathon project), use Turborepo. Your future self will thank you. And if Vercel enshittifies it in 2027? You can fork it (it’s open-source).


P.S. If you’re from the Lerna team and you’re reading this: Your tool was amazing in 2018. Then you abandoned it. Fix it, or more of us will leave. (We’re already leaving.)

P.P.S. To the Turborepo team: Thank you for building a build system that doesn’t take 45 minutes to build 12 packages. And please, for the love of all that is holy, don’t enshittify. We’ve seen how that movie ends. 🤐


Now go forth and build all the monorepos. At the speed of light. ⚡