go modules缓存失效的根本原因是github actions每次作业都从干净容器启动,导致默认的$gopath/pkg/mod路径(即/home/runner/go/pkg/mod)无法持久化,且actions/cache需显式配置路径与含go.sum哈希的key才能正确命中。

Go Modules缓存为什么在 GitHub Actions 里经常失效
根本原因不是 go mod download 没跑,而是默认工作目录下没有持久化的 pkg/mod 目录,每次 job 都从干净容器启动,GOPATH 和模块缓存全丢。GitHub Actions 的 actions/cache 不会自动识别 Go 缓存位置,必须显式指定路径。
- Go 1.11+ 默认把模块下载到
$GOPATH/pkg/mod,而 GitHub Actions Ubuntu runner 的$GOPATH是/home/runner/go,所以真实路径是/home/runner/go/pkg/mod - 缓存 key 必须包含
go.sum的哈希,否则依赖更新后仍用旧缓存,构建可能跳过校验、漏掉replace或exclude变更 - 别用
go env GOPATH动态读取 —— 在 cache action 的 restore 步骤里还不能执行 Go 命令,得硬编码或用${{ env.HOME }}/go/pkg/mod
正确的 GitHub Actions Cache 步骤写法
必须分三步:restore → download → save,且 restore 和 save 的路径、key 要严格一致。用 actions/cache@v4,别用老版本(v3 不支持自定义 cache hit 输出)。
- 缓存路径写死为
${{ env.HOME }}/go/pkg/mod,不要拼$GOPATH变量 - key 推荐用:
go-mod-v2-${{ hashFiles('**/go.sum') }},其中v2是手动版本号,升级 Go 或改缓存逻辑时改它来强制失效 - 在
restore后立即运行go mod download,确保即使缓存 miss 也能预热,避免后续 build 报missing module - save 步骤加
if: ${{ always() }},防止 download 失败时整个 job 卡住不存缓存
steps:
- uses: actions/cache@v4
with:
path: ${{ env.HOME }}/go/pkg/mod
key: go-mod-v2-${{ hashFiles('**/go.sum') }}
- run: go mod download
- uses: actions/cache@v4
if: ${{ always() }}
with:
path: ${{ env.HOME }}/go/pkg/mod
key: go-mod-v2-${{ hashFiles('**/go.sum') }}go mod download 和 go build 的顺序不能颠倒
很多人以为先 go build 再缓存就行,结果 CI 里频繁报 cannot find module providing package。这是因为 go build 在 module-aware 模式下会边编译边拉依赖,但只拉当前 build 需要的子集;而 go mod download 才会完整拉取 go.mod 里所有 indirect 依赖,保证缓存完整性。
- 如果项目有
replace指向本地路径(比如./localpkg),go mod download会跳过它,但缓存本身不受影响 —— 这类 replace 本就不该进缓存 - CI 中禁用
GOFLAGS=-mod=readonly,否则go build遇到缺失依赖直接失败,不会 fallback 到下载 - 交叉编译(如
GOOS=linux go build)不影响模块下载,go mod download是平台无关的
私有模块和 GOPROXY 配置容易漏掉
缓存本身不解决私有模块拉取问题,但配置错 GO_PROXY 会导致 go mod download 失败,进而让缓存始终 miss。GitHub Actions 默认没设代理,国内用户尤其容易卡在 golang.org/x/...
- 务必在
go mod download前设置GO_PROXY,推荐:https://proxy.golang.org,direct(海外)或https://goproxy.cn,direct(国内) - 私有仓库需额外配
GOPRIVATE,例如github.com/myorg/*,否则 proxy 会尝试转发,返回 404 或 403 - 不要在 cache key 里加入
GO_PROXY值 —— 它不影响模块内容哈希,加了反而导致同一份go.sum生成多个缓存碎片
缓存生效的关键,其实是 go.sum 哈希稳定 + 路径精准 + proxy 可达。少一个,CI 就退回裸下载。










