How big a server does a build cache need? CPU, RAM and disk
You are sizing a disk, not a compute tier. The server is one small program that idles in tens of megabytes, so the question is how much cache you want to keep.
Starting sizes
| vCPU | RAM | Disk | |
|---|---|---|---|
| Absolute minimum | 1 | 1 GB | cache cap + about 5 GB for the system |
| Recommended start, about 5 to 20 developers plus CI | 1 to 2 | 2 GB | 25 to 50 GB SSD |
| Larger CI farms | 2 to 4 | 4 to 8 GB | cache cap + about 20% |
These are starting points from our deployment guide. Your build graph decides the rest. It is hard to be limited by CPU, because the busy path is reading and writing stored files, not computing.
Set a size cap
Choose how much cache you want, set it with FSCACHE_MAX_BYTES, and make the disk about 20% bigger. When the cache reaches the cap it removes the least recently used entries and never grows past it. The extra room covers file system overhead and uploads in progress.
The value is in bytes. It is easy to be wrong by a factor of 1024, so copy one:
| Cap | FSCACHE_MAX_BYTES |
|---|---|
| 10 GiB | 10737418240 |
| 20 GiB | 21474836480 |
| 50 GiB | 53687091200 |
| 100 GiB | 107374182400 |
Two mistakes that look like "the cache doesn't help"
- A cap that is far too small, such as 20 MiB when you meant 20 GiB, makes it evict constantly, so the hit rate never climbs. The status page at
/statuszshows the cap it read next to current usage. - Leaving the cap at its default of 0, which means no limit, on a small disk is the one setup that will fill the disk.
What makes the disk grow
The cache grows only when a build makes an output the cache does not have yet. A build that changes nothing adds nothing. In our test, an edit inside one method added one small entry per build, about 2.5 KB. A new public method added four entries, about 11.5 KB, because the other three modules depend on it, directly or through another module, so all four were recompiled.
- To size your own: run your normal builds for a day, then run
du -skon yourFSCACHE_DATA_DIRfolder. It also counts a 128 KB index file and rounds each small entry up, so on a small project it reads higher than the entries alone. - To limit growth: set a size cap (see Set a size cap above). The oldest-unused entries go first; see build cache eviction and the size limit.
How we checked
| After build | No change: entries / bytes | Edit inside a method: entries / bytes | New public method: entries / bytes |
|---|---|---|---|
| 1 (first; the edited series include their first edit) | 13 / 36,090 | 13 / 36,101 | 13 / 36,174 |
| 2 | 13 / 36,090 | 14 / 38,636 | 17 / 47,725 |
| 6 | 13 / 36,090 | 18 / 48,768 | 33 / 93,901 |
| 11 | 13 / 36,090 | 23 / 61,434 | 53 / 151,657 |
Three series of eleven real Gradle builds on the four-module project from Gradle build cache for a monorepo. Each series used a new empty FosterStack Cache 0.2.1 server, a fresh project copy and a new Gradle home for every build, with Gradle's local cache off. Each edit was made to a clean copy, not on top of the last. We counted the stored entries (the files in the server's blobs folder) after each build. The project is tiny, so the sizes are small; the pattern is what carries over. Not tested: a real-sized project, tests, Maven, or the cap switched on.
Put it near the runners
On bigger setups the network matters more than the processor. A small server in the same region, ideally the same network, as your CI runners beats a large one far away.
Related: Gradle and Maven setup and Jenkins and CI build cache. Full detail is in the Docker deploy guide.