Cache Go Modules and Build Cache in GitHub Actions
Last updated
To cache Go in GitHub Actions, use actions/setup-go. It caches Go modules and build outputs by default, keyed on go.sum. There are two caches worth having: the module cache (GOMODCACHE, downloaded dependencies) and the build cache (GOCACHE, compiled packages and test results). The built-in cache covers both.
Option 1: setup-go
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v7
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- run: go build ./...
- run: go test ./...
Per the setup-go README (checked October 2026), cache defaults to true, and the action caches Go modules and build outputs. The default dependency file is go.mod at the repo root; set cache-dependency-path to go.sum to key on the checksums file, or to a glob when you have several modules:
- uses: actions/setup-go@v7
with:
go-version-file: go.mod
cache-dependency-path: |
go.sum
tools/go.sum
Turn it off with cache: false, which you might want on workflows that publish releases.
Option 2: explicit actions/cache
Ask Go where its caches are, so the paths are right on every OS:
- uses: actions/setup-go@v7
with:
go-version-file: go.mod
cache: false
- name: Find Go cache paths
id: go-cache
run: |
echo "mod=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT"
echo "build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT"
- uses: actions/cache@v6
with:
path: |
${{ steps.go-cache.outputs.mod }}
${{ steps.go-cache.outputs.build }}
key: go-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
go-${{ runner.os }}-${{ runner.arch }}-
- run: go test ./...
On Linux these are ~/go/pkg/mod and ~/.cache/go-build. Setting cache: false avoids two overlapping caches.
Two caches, two jobs
The module cache is keyed well on go.sum: it contains exactly what the checksums list. The build cache is different. Go's build cache is content-addressed: an entry is valid only if the source, compiler flags and dependencies hash the same. So a restored build cache from yesterday's commit is mostly still valid, and Go simply rebuilds what changed.
That means a prefix restore-keys fallback is especially useful for GOCACHE. A go.sum change doesn't invalidate most compiled packages; a stale entry is harmless because Go checks content hashes.
| Cache | Contents | Saves | Key on |
|---|---|---|---|
GOMODCACHE | Downloaded module sources | Network time | go.sum |
GOCACHE | Compiled packages, test results | Compile and test time | go.sum + fallback prefix |
Test results in GOCACHE are real: go test reports (cached) for packages whose inputs did not change. In CI that can skip whole packages, but only with the same flags. Note that -race builds use separate cache entries from plain builds, and -count=1 disables test result caching. If you need fresh results every run, expect that to cost time.
Speed-ups beyond the cache
go mod downloadas a separate step makes dependency time visible in logs.go test -raceis slower by design (often 2 to 10 times). In compiler.dev's measured run of React plus Go plus Postgres withgo test -race, CI went from 6m13s to 2m42s mostly through more CPU (see benchmarks).- Split packages across jobs with test sharding; Go's
go list ./...makes a natural split. -pcontrols build parallelism; the default is GOMAXPROCS, which tracks core count.- For huge repos, consider a remote cache (GOCACHEPROG is supported in recent Go versions) rather than pushing everything through the 10 GB GitHub cache.
How to verify a cache hit
- The setup-go step logs
Cache restored from key: setup-go-Linux-...on a hit, andCache not foundon a miss. Its post step logsCache saved. - Run
go build -xlocally or in a debug job: restored packages are not recompiled.go testprints(cached)for unchanged packages. go mod downloadshould finish in well under a second whenGOMODCACHEis warm.gh cache list --key setup-go --limit 20shows size and last-used.
Common mistakes
- Hashing
go.modwhen the module graph is ingo.sum. Both change together in most repos, but go.sum is the checksum record; key on it for explicit caches. - A vendored repo with a cache. With
go mod vendorcommitted there is no module download to cache. Cache onlyGOCACHE. - Caching
GOPATH/bin. Tools installed withgo installshould be keyed on tool version. - Several Go versions in one key. The build cache is partitioned by Go version internally, but mixing them in one entry wastes space. Put the version in the key for matrices.
- Saving only on PRs. Populate from
main. See Caching in GitHub Actions.
FAQ
Does setup-go cache by default?
Yes. The cache input defaults to true. Set cache: false to turn it off.
Should I cache GOCACHE or just modules?
Both. Module downloads are quick on a good network; compilation is where the time goes, and GOCACHE is what saves it.
Why does go test still run every time?
Test caching is skipped with -count=1, with changed files or environment variables the test reads, and for tests that open files outside the module. Check for -count=1 in your command.
How big does the Go cache get?
Usually a few hundred MB, larger with CGo or many test binaries. See cache limits.
Next steps
See also speeding up Go CI end to end. Cargo has a similar split: caching Rust builds. For an end-to-end checklist, see how to speed up GitHub Actions. compiler.dev runs the same job on 8 vCPU for $0.012 per minute; see pricing.
Related
- Cache Cargo and Rust Builds in GitHub ActionsCache the Cargo registry, git dependencies and target directory in GitHub Actions with Swatinem/rust-cache or actions/cache, and avoid bloated caches.
- Cache Gradle Builds in GitHub ActionsCache Gradle dependencies and build cache in GitHub Actions with gradle/actions/setup-gradle or actions/cache. Keys, read-only PRs, cleanup and checks.
- Cache npm Dependencies in GitHub ActionsCache npm dependencies in GitHub Actions with setup-node or actions/cache. Working YAML, the right key, why not node_modules, and how to confirm a hit.
- Cache pip Dependencies in GitHub ActionsCache pip in GitHub Actions with setup-python or actions/cache, plus the faster uv option. Keys, what not to cache, and how to verify a hit.