cmd/go: clean GOCACHE based on disk usage · Issue #29561 · golang/go · GitHub
Skip to content

cmd/go: clean GOCACHE based on disk usage #29561

Open

Description

@4a6f656c

The GOCACHE appears to lack a disk size limit - this is a problem in a space constrained environment and/or when running go on a disk that is nearing capacity. For example, on the openbsd/arm builder (which runs on a USB stick), the ~/.cache/go-build directory runs past several GB in a very short time, which then leads to various failures (git clone or go builds). The only option that I currently appear to have is to run go cache -clean regularly, in order to keep the cache at a respectable size. It seems that having a configurable upper bound would be preferable and/or free disk space based checks that prevent writes (e.g. evict then write) to the cache from failing when the disk is full due, partly due to a large GOCACHE:

[gopher@cubox ~ 102]$ du -csh ~/.cache/go-build                                                                                                                                                                                 
2.2G    /home/gopher/.cache/go-build
2.2G    total
[gopher@cubox ~ 103]$ df -h /home
Filesystem     Size    Used   Avail Capacity  Mounted on
/dev/sd1m      9.0G    8.5G   -6.0K   100%    /home
[gopher@cubox ~ 104]$ go build -o /tmp/main /tmp/main.go

/home: write failed, file system is full

/home: write failed, file system is full

/home: write failed, file system is full

/home: write failed, file system is full

/home: write failed, file system is full

/home: write failed, file system is full

/home: write failed, file system is full

/home: write failed, file system is full

/home: write failed, file system is full
[gopher@cubox ~ 105]$ du -csh ~/.cache/go-build          
2.2G    /home/gopher/.cache/go-build
2.2G    total
[gopher@cubox ~ 106]$ df -h /home                        
Filesystem     Size    Used   Avail Capacity  Mounted on
/dev/sd1m      9.0G    8.5G   -6.0K   100%    /home

Activity

  1. added
    NeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.
    FeatureRequestIssues asking for a new feature that does not need a proposal.
    on Jan 4, 2019
  2. added this to the Go1.13 milestone on Jan 4, 2019
  3. jayconrod commented on Jan 4, 2019

    @jayconrod
    Contributor

    I think this would be a good thing to have, especially since the cache will be required starting in Go 1.12. I don't think there's a fixed maximum cache size that would work for everyone, but maybe we could at least make eviction more aggressive when the disk is low on space.

  4. mvdan commented on Jan 4, 2019

    @mvdan
    Member

    We're no longer allowing GOCACHE=off starting in 1.12, so it might be a bit tricky to allow an arbitrary size limit. One could simply swap GOCACHE=off with GOCACHELIMIT=0, for example.

    I seem to remember that @rsc designed the cache to automatically evict based on modification time. Seems sound to me to also automatically evict if the disk is full enough that it might realistically hit errors if it builds a few large packages (say, if there's less than 500MB left).

  5. bitfield commented on Jan 5, 2019

    @bitfield

    This sounds like a bit of a can of worms. How do you check for remaining disk space in a cross-platform way? How much free space should trigger a cache eviction? How do you guarantee that, having evicted some cache, the build process won't still run out of disk anyway?

    If you're building on a small disk, then wouldn't running go cache -clean before each build be a better solution than trying to build disk space management into the build tool itself?

  6. mvdan commented on Jan 5, 2019

    @mvdan
    Member

    I personally don't know how easy it would be to control "does the disk have enough space left" in a sane and portable way. I was simply pointing out that since the current eviction algorithm takes into account timestamps, perhaps it should also use some data from the filesystem or disk. If we can make that work, we could avoid adding more knobs to the go tool.

    If you're building on a small disk, then wouldn't running go cache -clean before each build be a better solution than trying to build disk space management into the build tool itself?

    That might not be a great solution - what if you're building a very large project like Kubernetes? It might produce many hundreds of megabytes of build cache, so it's not unreasonable to think that it could on its own be enough to fill up some filesystems.

  7. bitfield commented on Jan 5, 2019

    @bitfield

    That might not be a great solution - what if you're building a very large project like Kubernetes? It might produce many hundreds of megabytes of build cache, so it's not unreasonable to think that it could on its own be enough to fill up some filesystems.

    Indeed. So this proposal would, at best, kick the problem slightly further down the road.

  8. modified the milestones: Go1.13, Unplanned on Jan 15, 2019
  9. changed the title [-]cmd/go/internal/cache: GOCACHE appears to lack disk size limit[/-] [+]cmd/go: clean GOCACHE based on disk usage[/+] on Jan 15, 2019
  10. modified the milestones: Unplanned, Go1.14 on May 9, 2019
  11. removed this from the Go1.14 milestone on Oct 9, 2019
  12. 31 remaining items

  13. jakebailey commented on Nov 16, 2025

    @jakebailey
    Contributor

    My team has been hitting this repeatedly, especially now with things like agent mode or similar where an LLM can be churning away trying things, or even just a normal dev scenario with a build in a watch mode. I can myself produce something like 200GB of files in the build cache a day. I have the space to not run out, but others on my team have less space and can actually fill up their drives.

    In CI, I have a build cache set up for the typescript-go repo that keeps the most recent build main for other jobs, and that cache for just one build is some 11GB on disk (big enough that it runs out of space in GHA runners because they have so little free space).

    It would be nice if there were some way to have this pruned more aggressively.

    Perhaps our repo is just super bad when it comes to cache sizes due to one of the issues linked above, too.

  14. seankhliao commented on Nov 16, 2025

    @seankhliao
    Member

    Now that caching is pluggable via a cacheprog: https://pkg.go.dev/cmd/go/internal/cacheprog
    I think it's something people can experiment with to manage their caches in different ways (offloading to shared storage in ci, or just pruning more aggressively locally)

  15. josharian commented on Nov 16, 2025

    @josharian
    Contributor

    It’s good that people can mess around with cacheprog, but that is a stopgap at best.

    The existence of tools to fix your tools does not mean that your core tools don’t need to work well out of the box.

    We should not ask users to manage this themselves.

  16. seankhliao commented on Nov 16, 2025

    @seankhliao
    Member

    I mentioned it more as an easier way people experiment / prove that there's a good / cross platform algorithm for managing the cache size, since the thread seems to be a lot of ideas and complaints, but very little in the way data on what would actually work.

  17. jakebailey commented on Nov 16, 2025

    @jakebailey
    Contributor

    Yes, I plan to try and build a GOCACHEPROG for this, both for the cleaning, but also because I've found that the actual cached files themselves are super compressible. One file that's part of the cache for TypeScript's AST package is 200MB, but compresses with zstd to just 14MB!

    I may have avoided this problem on my own personal machines just because I use btrfs with transparent zstd compression. My work machines do not do this.

  18. josharian commented on Nov 16, 2025

    @josharian
    Contributor

    Oooooh, transparent zstd is a fascinating idea.

  19. josharian commented on Nov 16, 2025

    @josharian
    Contributor

    Also, because GitHub ever-so-helpfully hides comments, I'm going to repeat in full here something Russ wrote above, for visibility.

    The main benefit of the current algorithm is simplicity: we just scan the tree for files that haven't been used in 5 days and delete them. We could drop the number of days, but the idea was to avoid a slow start after a long weekend. It is also possible to do incrementally and requires no global state, locks, or synchronization between different instances of the go command. It also automatically sizes to the working set rather than using a fixed amount of disk or thrashing in too small an amount of disk. I'm happy to consider other algorithms, but they need these properties too.

    Here is one possibility. The cache is basically a mark-sweep GC for old files, using the mtime as an auto-expiring mark bit to keep a file from being reclaimed for a while. We could instead use a generational GC, with a young generation and an old generation. If the cache is filling up with files that are used for a single build or only for a few minutes and then never used again, we could collect the young generation more aggressively. Specifically, we could keep a young/old bit in the mtime as well, perhaps in the low order bits. Every time we write a new cache file, set its mtime to time.Now.Truncate(time.Minute), so that it ends in :00 (0 seconds). If it gets used after an hour, we set the updated mtime to end in :30. Then the sweep can distinguish young vs old files and delete young files more aggressively.

    If someone has a usage pattern that creates a lot of cache and wants to try it out, patch in https://go.dev/cl/433295.

  20. jakebailey commented on Nov 16, 2025

    @jakebailey
    Contributor

    Oooooh, transparent zstd is a fascinating idea.

    Unfortunately, it feels like GOCACHEPROG cannot be used to do this since the protocol expects get to have written files to disk. So, no compression there; it would have to be done at the places the Go toolchain caches things. And it doesn't seem like the stdlib has zstd compression, only decompression. At best, a GOCACHEPROG could compress its own assets.

    For actually managing the lifetime of assets, it's not clear how a GOCACHEPROG can achieve this either without a lot of complication, given the requirement to write files back to disk. Since a GOCACHEPROG exe can be run from multiple concurrent unrelated builds, you basically end up redoing everything the Go disk cache already does to make that work, at least in my mind.

    I think it's probably the best situation if the built in disk cache got some sort of generational caching as linked above, and then some sort of compression where possible.

  21. josharian commented on Nov 16, 2025

    @josharian
    Contributor

    #62513 Is about adding zstd support to std in general. I believe @klauspost to be actively working on it (?). Nice to have this as a possible internal use case for it.

  22. jakebailey commented on Nov 17, 2025

    @jakebailey
    Contributor

    gzip seems to do alright at 19MB for my example, though in quite literally 20x more time (70ms vs 1.4s!).

    But, somewhat offtopic to the cleanup issue. I was just hoping that making the cache in general 10x+ smaller would help with this problem 😄

  23. AlekSi commented on Nov 17, 2025

    @AlekSi
    Contributor

    This discussion prompted me to finally cleanup our internal tool and publish it: https://github.com/AlekSi/hardcache

  24. 4a6f656c commented on Jan 1, 2026

    @4a6f656c
    ContributorAuthor

    Still hit this on a regular basis...

    $ ./all.bash
    ...
        --- FAIL: TestVet/unmarshal (1.41s)
            vet_test.go:201: error check failed: 
                unmarshal.go:17: missing error "call of Unmarshal passes non-pointer as second argument" (prefix: unmarshal.go:17)
                Unmatched Errors:
                write /home/joel/.cache/go-build/9d/9db2c193802a25eea297bdeebd583d38345371edc782b1dfc8fcc48294fcee62-d: no space left on device
    FAIL
    FAIL    cmd/vet 11.429s
    FAIL
    go tool dist: Failed: exit status 1
    $ df -h .
    Filesystem     Size    Used   Avail Capacity  Mounted on
    /dev/sd0g     15.5G   14.7G      0B   100%    /home
    $ go clean -cache
    $ df -h .         
    Filesystem     Size    Used   Avail Capacity  Mounted on
    /dev/sd0g     15.5G    8.4G    6.4G    57%    /home
    
  25. bysiber commented on Mar 2, 2026

    @bysiber

    This is one of those issues that seems niche but affects basically every Go developer over time. The build cache just keeps growing with no upper bound, and go clean -cache is too aggressive iut wipes everything.

    On macOS with a 256GB or 512GB SSD, GOCACHE can quietly eat 5-10GB over a few months of active development. Adding GOMODCACHE on top of that and you're looking at 15GB+ of Go-related caches.

    What I've been doing until this gets addressed:

    # Check current cache sizes
    du -sh $(go env GOCACHE)
    du -sh $(go env GOMODCACHE)
    
    # Remove build cache entries older than 30 days
    find $(go env GOCACHE) -type f -mtime +30 -delete
    
    # Check module cache
    du -sh ~/go/pkg/mod

    For macOS users dealing with this: ClearDisk is a free open-source menu bar tool that monitors GOCACHE, GOMODCACHE, and ~/go/pkg/mod alongside other developer caches (Rust target/, node_modules, Docker, Xcode DerivedData, pip). It shows exactly how much space each cache uses so you can make informed cleanup decisions.

    Having the go clean command support something like --max-size or --older-than would be great. The current all-or-nothing approach makes developers hesitant to clean anything, so caches just grow forever.

  26. jakebailey commented on Jun 26, 2026

    @jakebailey
    Contributor

    Unfortunately, it feels like GOCACHEPROG cannot be used to do this since the protocol expects get to have written files to disk.

    I forgot I wrote this, and in the context of #76337 I actually wrote a GOCACHEPROG to do this to a reasonable effect: https://github.com/jakebailey/gocachez

    Part of it is that it removes old files automatically up to a configurable size, which is harder to reach with compression as well.

  27. added a commit that references this issue on Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    FeatureRequestIssues asking for a new feature that does not need a proposal.GoCommandcmd/goNeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions