#81830·Go

cmd/go: build cache management

Author: seankhliaoCreated Sep 28, 2026Updated Sep 28, 2026
LabelsGoCommand

build cache primer

cache structure

The go build cache stores action and data entries used by various go commands. The cache is structured as:

$GOCACHE/
├── 00/
│   ├── <64-hex-ActionID>-a
│   ├── <64-hex-OutputID>-d
│   └── <64-hex-OutputID>-d/
│       └── <binary_name>
├── ...
├── fuzz/
│   └── <import/path>/
│       └── ...
├── trim.txt
├── testexpire.txt

In particular:

  • trim.txt records the last time the build cache was trimmed
  • testexpire.txt records a cutoff date for useable test cache artifacts (go clean -testcache doesn't actually delete test cache entries)

adding to the build cache

cache keys are a hex sha256 hash of cache inputs. This does mean that we can't know what is cached merely from looking at the cache entry. Data entries should take up the most space, and each should have a corresponding Action entry, but the action entry currently only records the hash mapping of action to output without additional details.

ai summary of artifacts and cache keys

3. What Goes into the Cache Key for Each Type of Cached Artifact?

Cache keys (ActionID) are 32-byte salted SHA-256 hashes generated by NewHash. Every hash starts with hashSalt (gover.LocalToolchain()), guaranteeing that different Go toolchain versions or development commits never collide on action keys. Derived subkeys use Subkey(parent, desc) = SHA256("subkey:" + parent + desc).

A. Package Compilation Action Key (Builder.buildActionID)
  • Salt: hashSalt
  • Tag: compile\n
  • Package origin: package directory (if outside GOROOT and not trimmed), or module <path>@<version> (if -trimpath)
  • Language version: go <version> from go.mod
  • Platform: goos <GOOS> goarch <GOARCH>
  • Package metadata: import <ImportPath>, omitdebug, standard, local, prefix, trimpath, forcelibrary
  • C toolchain configuration: C compiler ID, compiler flags, and toolchain hashes if package uses cgo (addCToolchainIDs)
  • Coverage: cover <mode> <toolID("cover")> (if coverage instrumentation enabled)
  • Fuzzing: fuzz instrumentation flags (if fuzzing enabled)
  • Module build info: modinfo <string>
  • Toolchain-specific configuration:
    • For gc: compiler tool ID (b.toolID("compile")), forced gcflags, package gcflags; asm tool ID, forced asmflags, package asmflags (if .s files present); architecture environment variables (GOAMD64, GOARM, GOMIPS, etc.); clean GOEXPERIMENT; compiler debug env vars (GOCLOBBERDEADHASH, GOSSAFUNC, GOSSADIR, GOCOMPILEDEBUG)
    • For gccgo: gccgo tool ID, flags, package path, ar tool ID, assembler tool ID
  • Input file contents: file path and SHA-256 content hash (FileHash) of every GoFiles, CgoFiles, CFiles, CXXFiles, FFiles, MFiles, HFiles, SFiles, SysoFiles, SwigFiles, SwigCXXFiles, and EmbedFiles.
  • Dependency export data: for each direct imported dependency, import <ImportPath> <buildExportID(dep.buildID)> (using the content ID of the dependency's export data, not its action ID, enabling convergence in circular bootstrapping).
  • Preprocessed PGO profile: pgofile <fileHash(pgoProfile)> (if PGO enabled).
B. Link Action Key (Builder.linkActionID)
  • Salt: hashSalt
  • Tag: link\n
  • Platform and mode: resolved buildmode, goos <GOOS>, goarch <GOARCH>
  • Package metadata: import <ImportPath>, omitdebug, standard, local, prefix, defaultgodebug, trimpath
  • Linker configuration: linker tool ID (b.toolID("link")), forced ldflags, package ldflags, arch env variables, clean GOEXPERIMENT, GOROOT (or empty if -trimpath), GO_EXTLINK_ENABLED
  • Dependency packages: for every dependency action a1, packagefile <ImportPath>=<buildObjectID(a1.buildID)> (content ID of package object data).
  • Main package ID: packagemain <buildActionID>/<buildObjectID> (ensures the binary's embedded build ID matches).
  • Shared libraries: packageshlib <ImportPath>=<buildObjectID(shlib)> (if linking against shared libraries).
C. Separate Export Action Key (Builder.exportActionID)
  • Salt: hashSalt
  • Export tool ID: b.toolID("export")
  • importPath, compiler, goVersion
  • Content hashes of all Go source files
  • Export content IDs of dependent packages (packageFile <ImportPath>=<buildExportID(dep.buildID)>).
D. Vet Analysis Key (Builder.vet)
  • Salt: hashSalt
  • Vet tool ID: b.toolID("vet")
  • Active vet checker flags (vetFlags)
  • Root package compile action ID: pkg <a.Deps[0].actionID>
  • Content hashes of .vetx fact export files of all dependencies (vetout <ImportPath> <fileHash(vetx)>).
  • Subkeys:
    • Fix archive: cache.Subkey(id, "fix.zip")
    • Output stdout: cache.Subkey(id, "stdout")
E. Test Result Keys (Two-Level Architecture)
  1. Level 1 Key (testID) (runTestActor.Act):
    • hashSalt
    • Content ID of test executable (or linker action ID proxy) id
    • Test command line flags (cacheArgs)
    • Exec command runner (work.ExecCmd)
    • Value stored: test execution log (# test log).
  2. Level 2 Key (testInputsID) (computeTestInputsID): Computed by parsing the recorded # test log:
    • env GODEBUG <hash>
    • getenv <var>: hash of environment variable existence and value
    • chdir <path>: stat hash of directory
    • stat <path>: file stat info (size, mode, mtime, isDir)
    • open <path>: file stat info (plus directory entry stats if directory; rejects caching if modified < 2s ago via errFileTooNew)
  3. Lookup Keys:
F. Cgo Run & Compile Keys
  • cgoRunActionID (exec.go): cgo tool ID, GOOS, GOARCH, import path, package directory, C/C++/Fortran compiler flags, msan/asan flags, export header flag, and hashes of Cgo/SWIG files.
  • cgoCompileActionID (exec.go): GOOS, GOARCH, source file name and content hash, compiler flags, package origin, trimpath flag, C toolchain IDs.
  • Subkeys: Intermediate object files stored under cache.Subkey(actionID, filename).
G. Preprocessed PGO Profile Key (Builder.pgoActionID)
  • Salt: hashSalt
  • Preprofile tool ID: b.toolID("preprofile")
  • Content hash of raw input pprof file (b.fileHash(input)).
H. C Compiler Flags & Compiler ID
  • gccCompilerID (exec.go): Hash of compiler binary path, stat info of compiler binaries, and probed internal compiler ID.
  • gccSupportsFlag: cache.Subkey(compilerID, "gccSupportsFlag " + flag).
I. Module Index Keys (modindex)
  • Module hash: runtime.Version(), index format version, modroot.
  • Package hash: runtime.Version(), index format version, modroot, pkgdir, directory entries and stat info (rejects caching if mtime is within 2 seconds).

removing from the build cache

On every command exit, trim.txt is checked. Cache trimming runs at most once per day. Entries with mtime older than 5 days are removed.

Accessing a cache entry updates mtime (not atime).

go clean -cache drops the entire cache:

  • concurrent clean and build is unsafe #31948
  • cache can be trimmed while still in use #69566

problems people have

cache doesn't cache enough

  • cache test binaries #77349
  • cache should persist for longer (to have a warm cache in containerized / reproducible builds) #69565

cache caches too much

  • caches large embedded resources #59683
  • generics bloats build cache, in a low level dep #76946
  • make.bash cache entries #61484
  • runs out of disk space
    • compress the build cache #76337
    • clean the build cache based on size (absolute limit, disk relative limit) #29561
  • cache trimming takes a long tim #38939

what to do?

A size based cached could solve both wanting to cache more, and caching too much. But it would be adversely affected by storing a lot of recent large binaries (e.g. lots of test runs).

A generational cache like https://go.dev/cl/433295 could let us prune churning entries more effectively.