improvement(ci): bound the local Turborepo cache (#7214)

Turborepo's local cache eviction is opt-in until 3.0, so without cacheMaxAge and
cacheMaxSize the filesystem cache grows forever. In CI each cache dir lives on a
Blacksmith sticky disk that is mounted many times a day, so it never idles long
enough for Blacksmith's own 7-day inactivity purge to fire, and one cache-missing
app build writes a ~400 MB artifact. The build cache disk reached 206 GB over 43
days — roughly 4.8 GB/day of sediment — at ~$0.51/GB-month.

Size is the real bound; age is hygiene. A cache hit only happens when a task's
input hash is unchanged, which recurs within hours, not weeks, so nothing written
days ago can ever be read again — the sibling PR-keyed disk does the same job in
30 GB. The cache still earns its keep: the app build hits ~17% of the time and a
hit saves ~7 minutes, so this is a ceiling, not a removal. That distinguishes it
from the Turbopack persistent cache, which was removed because it measured 3.2x
SLOWER; this one is measurably faster, just unbounded.

Set in turbo.json rather than per-step env vars so there is one source of truth
and turbo validates it. Verified against the installed 2.9.14: both keys are in
its schema, turbo parses them, and the task hash is byte-identical with and
without them, so enabling eviction does not invalidate the existing cache.
This commit is contained in:
Waleed
2026-08-28 02:08:08 -07:00
committed by GitHub
parent dca1fd60be
commit db25446b35
+17
View File
@@ -1,6 +1,23 @@
{
"$schema": "https://v2-9-12.turborepo.dev/schema.json",
"envMode": "loose",
// Local cache eviction is opt-in until Turborepo 3.0, so without these the
// filesystem cache grows forever. In CI each cache dir is a Blacksmith sticky
// disk that is mounted many times a day, so it never goes idle long enough for
// Blacksmith's own 7-day inactivity purge to fire, and one cache-missing app
// build writes a ~400 MB artifact: the build cache reached 206 GB in 43 days.
//
// Size is the real bound and age is hygiene. A hit only happens when a task's
// input hash is unchanged a re-run, or a commit touching only unrelated
// workspaces which recurs within hours, so nothing older than a day or two
// can ever be read again. Measured hit rate on the app build is ~17%, worth
// ~7 minutes each, so the cache earns its keep; it just needs a ceiling.
// 40 GB is ~100 app-sized artifacts against ~4.8 GB/day of real accumulation.
//
// Neither key participates in the task hash, so changing them does not
// invalidate the cache.
"cacheMaxAge": "7d",
"cacheMaxSize": "40GB",
"tasks": {
"transit": {
"dependsOn": ["^transit"],