The one with loggers in Golang
A simple guide to write rotating loggers in Golang.
An architectural look at GOMODCACHE, CI caching, private modules, and the mistakes that make Go builds slower and less predictable.
GOMODCACHE is one of those Go concepts teams rely on heavily without thinking about it deeply. That usually works until build times become inconsistent, CI jobs start redownloading too much, or a supposedly reproducible environment behaves differently on a new runner.
At that point, the module cache stops being an implementation detail and becomes an operational concern.
This post is about the gotchas that show up when GOMODCACHE is treated casually.
GOMODCACHE is not the same as the build cache #This is the first misunderstanding to fix.
GOMODCACHE stores downloaded module sourceGOCACHE stores compiled build artifactsTeams often tune one and expect results from the other.
If a CI pipeline repeatedly downloads dependencies, the problem is probably cache persistence or dependency resolution behavior. If the pipeline downloads nothing but still recompiles too much, the issue is likely build cache invalidation.
Treating these as the same cache leads to wasted debugging.
The module cache makes local development feel stable because once dependencies are present, many builds appear deterministic.
But cached success can hide real problems:
go.sumreplace directives depending on local layoutThen the build moves to a clean environment and fails.
This is why clean-runner validation matters. A warm GOMODCACHE is useful for speed, but it should not be the only environment you trust.
This is the most common operational mistake.
If the cache key is too coarse, unrelated branches or incompatible states reuse the same module cache and create noisy behavior. If it is too narrow, the cache misses constantly and you lose the benefit entirely.
A practical cache key usually needs to reflect:
go.sumThat balance keeps cache reuse meaningful without turning it into accidental shared state.
The module cache is portable in many cases, but teams still get burned when they share it too aggressively across environments with meaningful differences.
Be careful when mixing:
The cache may still work often enough to look healthy while creating hard-to-explain misses or odd fetch behavior.
If you want reliable CI performance, partition the cache along real compatibility boundaries.
GOMODCACHE turns into a policy problem #Public module workflows are comparatively easy. Private modules expose the real discipline of the setup.
Common issues:
GOPRIVATE not configured correctlyWhen this happens, teams blame the cache. In reality, the cache is just revealing that dependency access is not modeled explicitly.
For private modules, your operating model should be clear:
Without that, GOMODCACHE becomes an unreliable convenience layer instead of a trusted accelerator.
Developers often reach for go clean -modcache as a universal fix. It can help confirm whether the cache is masking a dependency problem, but it should not be your normal recovery strategy.
If regularly deleting the cache is the only way to keep builds healthy, the real issue is usually one of:
Wiping the cache hides the operating problem while making performance worse.
GOMODCACHE #The classic Docker optimization is to copy go.mod and go.sum early, run go mod download, and preserve that layer. That works well, but only if the Dockerfile structure reflects dependency stability correctly.
The common mistakes are:
This is where GOMODCACHE becomes a build-architecture decision, not just a Go flag.
Some teams assume the module cache will “just help” in constrained networks. It helps only if the dependency population strategy is deliberate.
Questions worth answering:
Once network reliability is part of the build story, GOMODCACHE becomes a supply path concern.
A healthy module cache cannot rescue a weak dependency policy.
You still need:
The cache is there to reduce repeated work. It should not be carrying the burden of dependency correctness.
What good usually looks like:
GOMODCACHE and build cache are treated separatelygo.sum, Go version, and runtime compatibilityThat setup is not glamorous, but it removes most of the pain teams attribute to “Go cache weirdness.”
GOMODCACHE is powerful because it makes Go feel fast and stable. The risk is that teams stop thinking about what it actually represents: local dependency state.
If that state is:
then the cache becomes an asset.
If not, it turns into one more layer of hidden build behavior that nobody understands until CI slows down or breaks.
Related posts
A simple guide to write rotating loggers in Golang.