GitHub Actions 缓存不命中排查与修复:actions/cache 实战指南

主题: github-actions-cache-not-found-fix更新于: 2026/7/28作者:AgentFactory 技术团队

快速答案

  • 核心结论:GitHub Actions 缓存不命中的根本原因通常是 cache key 不稳定、缓存路径错误或分支作用域限制,而非 actions/cache 本身的问题。
  • 第一排查点:检查 cache key 是否包含 hashFiles('**/package-lock.json') 且该文件已提交到仓库;确认 path 参数指向工具的实际缓存目录(如 ~/.npm)而非 node_modules
  • 最小修复方案:在 actions/cache@v4with 块中同时设置 keyrestore-keys,确保精确匹配失败时有回退机制。
  • 适用环境:GitHub Actions 所有运行环境(ubuntu-latest、windows-latest、macos-latest),actions/cache@v3 及以上版本。

它解决什么问题 / 适用场景

actions/cache 是 GitHub Actions 官方提供的缓存动作,用于在 CI/CD 工作流中缓存依赖项和构建产物,从而加速后续运行。它解决的核心问题是:

  • 重复下载依赖:每次 CI 运行都从远程注册表下载所有依赖(如 npm、pip、Maven),浪费带宽和时间。
  • 构建产物复用:缓存编译后的二进制文件或测试报告,避免重复编译。

适用场景

  • Node.js 项目(缓存 ~/.npmnode_modules
  • Python 项目(缓存 ~/.cache/pip 或虚拟环境)
  • Java/Gradle 项目(缓存 ~/.gradle/caches
  • Go 项目(缓存 ~/go/pkg/mod
  • 多分支开发、低流量仓库(缓存易过期,需额外注意)

核心配置 / 参数说明

actions/cache 有三个核心参数,下表详细说明每个参数的作用和最佳实践:

参数是否必填说明示例值最佳实践
path要缓存的文件或目录路径,支持通配符~/.npm缓存工具的实际缓存目录,而非项目目录
key缓存唯一标识,通常包含 runner OS 和 lock 文件 hash${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}使用稳定标识符,避免包含 github.sha 或时间戳
restore-keys精确 key 未命中时的回退 key 列表,按顺序尝试${{ runner.os }}-npm-至少提供一个前缀回退,确保跨分支缓存共享

完整配置示例(Node.js 项目)

YAML
- name: Cache npm dependencies
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-npm-

关键要点

  1. path 设置为 ~/.npm 而非 node_modules,因为 npm ci 会先删除 node_modules 再安装,缓存 node_modules 会导致浪费。
  2. key 使用 hashFiles('**/package-lock.json'),只有依赖变化时 key 才会改变。
  3. restore-keys 提供前缀回退,即使精确 key 不匹配,也能恢复最近的缓存。

与同类方案对比

对比维度actions/cachesetup-node 的 cache 输入手动缓存(如 tar/upload-artifact)
缓存命中率高(支持 restore-keys 回退)中(仅精确匹配)低(需手动管理 key)
配置复杂度中等(需理解参数)低(一行配置)高(需编写脚本)
跨分支缓存共享支持(通过 restore-keys)不支持需手动实现
缓存路径灵活性高(任意路径)低(仅工具默认路径)高(任意路径)
对 lock 文件的依赖强(推荐使用 hashFiles)强(自动检测)无(需自行设计 key)
缓存过期策略7 天未访问自动清除同 actions/cache同 actions/cache

结论actions/cache 提供最灵活的缓存控制,适合需要精细管理缓存的场景;setup-nodecache 输入适合快速配置的简单项目。

生产环境实践与注意事项

生产部署限制

  1. 缓存作用域为分支:非默认分支的 PR 首次构建必然 miss,因为缓存只能被同一分支或默认分支的工作流读取。
  2. 缓存自动清除:缓存 7 天未访问即被 GitHub 自动清除,低流量仓库需定期触发构建。
  3. 仓库总缓存上限 10GB:超出后旧缓存被逐出,需监控缓存使用量。
  4. 缓存路径必须准确:恢复成功但无加速效果,通常是因为 path 指向了错误目录。
  5. 并发构建冲突:多个 job 同时保存相同 key 的缓存可能导致写入冲突,建议在关键 job 中使用 save-always: false

安全性建议

  • 避免缓存敏感文件:不要缓存 .env、密钥、证书等敏感文件。
  • 缓存内容应仅包含依赖和构建产物:不要缓存源代码或配置文件。

跨分支缓存共享策略

YAML
- name: Cache npm dependencies
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-npm-

关键点restore-keys 中的 ${{ runner.os }}-npm- 前缀会匹配默认分支(如 main)上保存的缓存,即使 PR 分支的精确 key 不同,也能恢复最近的缓存。

常见报错与排查

报错 1:Cache not found for input keys

错误信息Cache not found for input keys: Linux-node-abc123def456...

根因:cache key 不稳定或 lock 文件未提交。

解决方案

  1. 检查 hashFiles 引用的文件(如 package-lock.json)是否已提交到仓库。
  2. 确保该文件未被工作流中的其他步骤重新生成。
  3. 添加 restore-keys 作为回退。

报错 2:Cache restored successfully but node_modules is still empty

错误信息:缓存恢复成功,但 npm ci 仍然重新下载所有依赖。

根因:缓存路径错误,缓存了 node_modules 而非 ~/.npm

解决方案

  1. path 改为 ~/.npm(npm 的全局缓存目录)。
  2. 使用 npm ci 而非 npm install,因为 npm ci 会从缓存中安装。

报错 3:Cache key changes on every run despite no dependency changes

错误信息:每次运行 key 都不同,即使依赖没有变化。

根因hashFiles 引用了不稳定的文件(如 package.json 被生成步骤修改)。

解决方案

  1. 改用 package-lock.jsonyarn.lock 作为 hash 源。
  2. 确保该文件已提交到仓库,且不被工作流修改。

报错 4:Cache saved on a different branch and not available

错误信息:缓存保存在其他分支,当前分支无法读取。

根因:缓存作用域为分支级别。

解决方案

  1. 确保工作流在默认分支(如 main)上运行,因为默认分支的缓存对所有分支可读。
  2. 在 PR 工作流中添加 restore-keys 以匹配默认分支的缓存前缀。

常见问题 FAQ

Q: 为什么我的 npm 缓存明明恢复了,但 npm ci 还是重新下载了所有依赖?

A: 最常见的原因是缓存路径错误。如果你缓存了 node_modules 而不是 ~/.npmnpm ci 会先删除 node_modules 再安装,导致缓存浪费。正确的做法是缓存 ~/.npm(npm 的全局缓存目录),然后运行 npm ci,它会从缓存中快速安装依赖。

Q: 我的 PR 构建总是缓存 miss,但 main 分支构建正常,怎么办?

A: GitHub Actions 的缓存作用域是分支级别的,PR 分支只能读取目标分支(通常是 main)的缓存。确保 main 分支的工作流已经运行并保存了缓存。在 PR 工作流中添加 restore-keys 回退到 main 分支的缓存前缀,例如:restore-keys: ${{ runner.os }}-npm-。这样即使精确 key 不匹配,也能恢复最近的缓存。

Q: 缓存 key 中是否应该包含 github.sha 或时间戳?

A: 不应该。包含 github.sha 或时间戳会导致每次提交都生成不同的 key,从而完全破坏缓存机制。正确的做法是使用稳定的标识符,如 runner.os 和 lock 文件的 hash(如 hashFiles('**/package-lock.json')),这样只有依赖变化时 key 才会改变。

相关深度解决方案

在配置当前服务时,如果您需要实现更复杂的架构或多源数据整合,建议配合参考我们整理的 Next.js 14 构建失败:Webpack 内存溢出与配置错误的实战修复

在配置当前服务时,如果您需要实现更复杂的架构或多源数据整合,建议配合参考我们整理的 npm ERR! code EINTEGRITY 修复:缓存损坏与哈希校验失败排查指南