Turborepo at 7 Apps: Build Caches, Parallel Pipelines, and the Migrations That Hurt
Seven Next.js apps, fifteen packages, one Turborepo. Here's what actually matters after a year of running it.
The cache is the product
turbo run build with a warm cache completes in seconds for unchanged apps. Our full clean build runs under two minutes across seven apps; incremental builds touch only what changed.
The cache also means CI runs the same graph as your laptop. Same hashes, same inputs — a green local build should be a green CI build. When it isn't, that's a bug in your pipeline definition, not your code.
What bit us
Frozen lockfiles are non-negotiable. A CI run with pnpm install instead of --frozen-lockfile once installed a drifted dependency graph and the failures were unreproducible locally. Never again.
Stale .next caches after a Next.js upgrade. One upgrade left turbopack font modules pointing at a path that no longer existed. Symptoms were wild: the build failed, then passed, then failed on a different app. The fix was boring and effective: rm -rf apps/*/.next and rebuild. Caches are great until they're corrupted; when in doubt, delete them.
Shared packages are blast radius. A bug in @vietprohub/lib doesn't break one app — it breaks seven. The flip side: one fix improves seven apps. We treat shared package changes with the same review rigor as infrastructure.
The boring advice
- Pin everything:
--frozen-lockfile, engine fields, Node version in CI. - Keep task outputs declared so the cache works — missing
outputsinturbo.jsonsilently disables caching. - Delete caches at the first sign of weirdness; debugging turbopack internals is not a good use of time.
Monorepos are mostly about discipline. The tooling is fine; the discipline is the hard part.