Replacing actions/setup-go cuts Go CI test runtimes by 69%
Scaling Golang CI by Replacing actions/setup-go

CloudX open-sourced cloudx-io/setup-go, a drop-in replacement for GitHub's actions/setup-go. The default action's cache key ignores job identity, so parallel lint and test jobs race to write stale caches, causing test runtimes to balloon. The new action includes the job identity and run ID in the cache key, ensuring fresh caches every run. In a monorepo backtest, it cut test job runtimes by 69% and reduced unnecessary test package runs by 86%.
Eliminating the race by separating caches for our various jobs immediately solved this problem: we introduced cloudx-io/setup-go, test jobs resumed loading appropriate caches, and the median job runtime fell to 41 seconds, a 69% improvement.
- wannabe44
I always advocate having custom-built docker images for CI, periodically refreshed for security fixes. CI should not run more than few seconds over the standard time to run the same thing from a dev machine.
However, other people around me are fine with apt installs and pip installs from global mirrors in every CI run. So I may be just autistic.
- lukasschwab
There are some good off-cuts that didn't make the official post, but which might be of interest to HN!
It only gets a brief mention, but the cache-pruning change was an interesting one. Cache accretion happens in the default actions/setup-go too, but dramatically increasing the number of cache-writes for cloudx-io/setup-go made it an actual issue.
As the cache grows, so does the time it takes to load it from GitHub's actions cache... and that grows until it's a significant time-suck in CI. We prune with basic mark-and-sweep.
Digging deeper, the pluggable `GOCACHEPROG` (introduced in Go 1.24) is a really useful tool. Shimming the normal cache logic for measurement, for example. In theory this should also be attractive for remote caching.
- peterldowns
Hey everyone, one of the authors here. This is a "small" improvement that has saved us a LOT of developer time over the last few months. It's actually quite crazy to me that the default actions/setup-go simply does not work well if you want to have more than one golang action running at the same time.
The blogpost has a lot of technical details, but you can also just read the code and try it yourself: