---
title: "GitHub Actionsのキャッシュ｜actions/cacheのkey設計と復元されない原因の切り分け"
url: "https://www.issoh.co.jp/tech/details/16933/"
published: 2026-08-25
updated: 2026-09-28
categories: ["GitHub"]
publisher: "株式会社一創"
---

# GitHub Actionsのキャッシュ｜actions/cacheのkey設計と復元されない原因の切り分け

`actions/cache`は、依存パッケージのダウンロード結果をジョブ間で使い回すためのアクションです。3行で動きますが、`key`の組み立てを誤ると「毎回ミスして遅いまま」か「古い依存を復元し続けて壊れる」のどちらかへ倒れます。この記事では探索の3段階、key設計の型、npm・pip・Gradleでの対象パス、既定10GBという寿命の規則、復元されないときの切り分け、入れないほうが速い場面までを扱いました。ワークフローの基本構造は[GitHub Actionsとは？できること・使い方とCI/CD自動化の解説記事](https://www.issoh.co.jp/column/details/3005/)、テスト実行の組み方は[GitHub Actionsでビルド・自動テストを設定する方法](https://www.issoh.co.jp/tech/details/2497/)を先に押さえてください。

## まとめ：actions/cacheで先に決める4つの設計

第一に、キャッシュは`key`の完全一致だけで決まりません。外れたら部分一致、それも外れたら`restore-keys`の接頭辞一致という3段階で探されます。`key`は「毎回変わる識別子」、`restore-keys`は「変わっても当たる受け皿」という別の役割を担います。

第二に、キャッシュは作成後に書き換えられません。同じ`key`で保存しても既存が残り、新しい内容は捨てられます。ロックファイルのハッシュを`key`の末尾へ入れるのは、この不変性を回避して依存の更新を反映させるためです。

第三に、寿命は容量と時間の両方で切られます。リポジトリあたり既定10GBを超えると最終アクセス日の古いものからLRUで削られ、7日間参照されなかったものは容量に余裕があっても消えます。週に一度しか回らないワークフローのキャッシュは、当たらない前提で設計してください。

第四に、入れれば速くなるとは限りません。復元と展開に20秒かかる構成で再取得が15秒なら、キャッシュは純粋な損です。判断は「ダウンロード時間 － 復元時間」の実測で行ってください。2026年8月25日時点の最新メジャーは`v6.1.0`で、`v5`系以降はNode.js 24ランタイムとActionsランナー2.327.1以上を要求します。

## actions/cacheがキャッシュを探索して復元する3段階の順序

アクションが何をどの順で探すかを先に押さえます。ここを取り違えたまま`restore-keys`を書くと、当たっているつもりで毎回フルダウンロードという状態が続きます。

### exact一致からrestore-keysの接頭辞一致へ移る探索

公式ドキュメントは探索順を、完全一致の`key`、`key`の部分一致、`restore-keys`の部分一致と明記しています。`restore-keys`の定義は「順序付きの複数行文字列で、`key`でヒットしなかったときに古いキャッシュを復元するために使う接頭辞一致のキー群」です。前方一致が複数あれば、直近に作られたものが選ばれます。

書き方はこうなります。`key`に`Linux-node-9f3c…`のようなハッシュ付きの識別子を置き、`restore-keys`には`Linux-node-`だけを置く。ロックファイルが更新された週は完全一致が外れますが、先週のキャッシュが接頭辞一致で降りてくるので、差分だけを取りに行けば済みます。

### cache-hitがtrue・false・空文字で示す3つの復元結果

出力`cache-hit`は真偽値だと思われがちですが、実際は3状態です。READMEの記述は「キャッシュヒットがあれば`key`の完全一致かどうかを示す`true`または`false`になる。キャッシュミスなら空文字列になる」です。

整理すると、完全一致で復元できた場合が`true`、`restore-keys`の接頭辞一致で古いキャッシュを拾った場合が`false`、何も復元できなかった場合が空文字になります。`cache-hit != 'true'`という条件をよく見かけるのは、部分一致のときにも依存解決を走らせたいからです。ここを`== 'false'`と書くと、完全なミスの回で処理が飛ばされます。

### ジョブ成功時にしか保存されずkeyの上書きも効かない仕様の制約

新しいキャッシュが作られるのは、ジョブが成功して完了したときだけです。テストが落ちた回のキャッシュは残りません。失敗が続く間は依存の取得も毎回発生し、修正サイクルが遅くなります。保存だけを切り出したいなら、`actions/cache/save`を独立したステップとして`if: always()`付きで置く構成へ切り替えてください。

もうひとつの制約が不変性です。既存の`key`へ保存しようとしても上書きはされず、ログに既存キャッシュがある旨が出て終わります。「キャッシュを更新したい」という要件は、`key`を変える操作でしか満たせません。復元と保存を分けて制御したいときは、`actions/cache/restore`と`actions/cache/save`を使い分けます。

## keyとrestore-keysで作る階段構成とhashFilesの当て方

探索順が分かれば、key設計は機械的に決まります。要素を3層に分け、上から順に固定度の高いものを並べる形です。

### runner.osと依存種別とロックファイルのハッシュで組む3層

公式サンプルの`runner.os`・`node`・`hashFiles`という並びが、この3層です。第1層の`runner.os`はOS識別子で、LinuxとWindowsでバイナリ互換がないため必須になります。第2層の`node`は依存の種別で、同じリポジトリでnpmとpipを両方キャッシュするときの衝突を防ぎます。第3層のハッシュが唯一の可変部分です。

層を増やすなら、言語バージョンを第2層と第3層の間へ挟みます。Node.js 20と24でビルド済みのネイティブモジュールが混ざる事故は、キーへ`node20`と埋め込むだけで防げます。逆に、コミットSHAや実行番号を`key`へ入れてはいけません。毎回ユニークになるため完全一致は永久に発生せず、保存だけが積み上がって10GB枠を食い潰します。

### restore-keysの接頭辞を段階的に短くする階段の作り方

受け皿は1本ではなく階段にします。ロックファイルのハッシュを落とした`Linux-node20-`を1段目、言語バージョンも落とした`Linux-node-`を2段目に置く、という並べ方です。上から順に評価されるので、より近いキャッシュが優先されます。

段は3つまでで十分です。それ以上短くすると、OSだけが一致する無関係なキャッシュを引き当て、展開後に整合しません。とくに`Linux-`という1要素だけの受け皿は置かないでください。npm用のジョブがpip用のキャッシュを復元する事故が起こります。

### hashFilesの対象をロックファイルに絞って誤ヒットを防ぐ

`hashFiles`は指定パターンに一致した全ファイルの内容からハッシュを作ります。対象を`package.json`にすると、バージョン範囲は同じまま解決結果だけが変わったケースを検知できません。逆にソースディレクトリのような広いパターンを混ぜると、1文字直しただけで毎回ミスします。

当てる先はロックファイルだけに絞ります。npmなら`package-lock.json`、Pythonなら`requirements.txt`か`poetry.lock`、Gradleなら`gradle`系ファイルと`gradle-wrapper.properties`を併記する形です。モノレポで複数のロックファイルが散在する場合、ワイルドカード指定は一致した全ファイルを1つのハッシュにまとめるため、どれか1つの更新で全体がミスになります。パッケージ単位で当てたいなら、ジョブを分割して別の`key`を与えてください。

## npmとpipとGradleでキャッシュ対象に指定する実際のパス

ここは間違えると効果が出ないか、逆に壊れます。言語ごとに対象が違うためです。

### npmはホームの.npmを対象にしnode\_modulesを含めない

npmでキャッシュするのは`node_modules`ではなく、グローバルキャッシュディレクトリです。LinuxとmacOSではホーム直下の`.npm`、Windowsでは別パスになるため、公式サンプルは`npm config get cache`の出力を変数へ取って`path`に渡します。

`node_modules`そのものを保存しない理由は、READMEに明記されています。Nodeのバージョンをまたぐと壊れる可能性があり、`npm ci`とも噛み合わないためです。`npm ci`は実行前に`node_modules`を削除するので、復元したディレクトリはそのまま捨てられます。グローバルキャッシュを当てておけば、ネットワークへ出ずローカルから展開するため短縮は得られます。

### pipのキャッシュ先がOSごとに分かれる3つのパスと指定方法

pipのキャッシュ先はOSで異なります。3OSのマトリックスを回すなら、ランナーごとに`path`を切り替える必要があります。

| ランナー    | pipのキャッシュ先                 | keyの第1層 |
| ------- | -------------------------- | ------- |
| Ubuntu  | ホーム直下の .cache/pip          | Linux   |
| macOS   | Library/Caches/pip         | macOS   |
| Windows | AppData\\Local\\pip\\Cache | Windows |

3つを`path`へ並べても、存在しないパスは無視されるため動きます。ただしWindowsで作ったキャッシュを他OSで復元するには`enableCrossOsArchive`を`true`にする必要があり、既定は`false`です。OSごとに`key`を分けるほうが素直でしょう。ランナーの選び方そのものは[GitHub Actionsのruns-on｜ラベル指定の記法と-latest更新・arm64への備え](https://www.issoh.co.jp/tech/details/16929/)で扱っています。

### Gradleはcachesとwrapperを並べデーモン停止を確認

Gradleはホーム直下`.gradle`配下の`caches`と`wrapper`を`path`へ並べます。前者が依存とビルドキャッシュ、後者がGradle本体の配布物です。wrapperを外すと、毎回100MB前後のディストリビューションを取りに行きます。

Gradle固有の落とし穴が、デーモンのロックです。公式サンプルには「ワークフロー完了時にGradleデーモンが残っていないことを確認してください。ロックが保持されているとキャッシュパッケージの作成に失敗する可能性があります」という警告が添えられています。CIでは`--no-daemon`を付けるか、ビルド後に`gradle --stop`を挟みます。Mavenはホーム直下`.m2`の`repository`が対象です。

### setup系アクションのcache入力で置き換えられる範囲と限界

言語セットアップ用のアクションには、キャッシュ機能が内蔵されています。対象は`setup-node`（npm・Yarn・pnpm）、`setup-python`（pip・pipenv・Poetry）、`setup-java`（Gradle・Maven）、`setup-ruby`、`setup-go`、`setup-dotnet`の6つです。`cache: npm`と1行書けば、パスもキーも自動で決まります。setup-nodeで自動キャッシュが有効になる条件と、公開ジョブで切る判断は[actions/setup-nodeの入力・キャッシュ設定を解説した記事](https://www.issoh.co.jp/tech/details/17919/)にまとめています。

依存キャッシュしか要らないなら、これで足ります。`actions/cache`を明示的に使うべきなのは、ビルド生成物を持ち回りたいときです。Next.jsのビルドキャッシュ、Rustの`target`、Playwrightのブラウザバイナリは組み込みの対象外なので、自前で`path`と`key`を書きます。共通の定義を複数リポジトリで回したいなら、[Reusable Workflows（GitHub Actions）とは？workflow\_callでの作成・呼び出しの解説](https://www.issoh.co.jp/tech/details/10765/)にある呼び出し側と定義側の分離が使えます。

## 10GBの上限とLRU退避と7日ルールが決めるキャッシュの寿命

設計が正しくても、保存したものが残っていなければ当たりません。規則は3つです。

### 既定10GBとユーザーリポジトリで10TBまで広げられる設定

キャッシュ枠は既定でリポジトリあたり10GBです。アカウント単位ではなくリポジトリ単位で、FreeでもEnterprise Cloudでも既定値は変わりません。ユーザー所有のリポジトリなら最大10TBまで引き上げられ、Organization配下とEnterprise配下の上限はそれぞれの設定で決まります。

引き上げれば追加のストレージ課金が発生します。金額の実数と請求の積み上がり方は[GitHub Actionsの料金｜2026年改定後の分単価・無料枠とコスト削減の判断基準](https://www.issoh.co.jp/tech/details/16698/)にまとめてあるので、見積もりに載せるならそちらを参照してください。実務では、上限を上げる前に不要なキャッシュ定義を削るほうが確実に効きます。依存関係ではなくビルド成果物を運んでいる定義が混ざっていないかは、[GitHub Actionsのアーティファクト｜受け渡しの設計と同名不可・保持期間の決め方](https://www.issoh.co.jp/tech/details/16951/)の判断軸で切り分けられます。

### 最終アクセス日の古い順に消えるLRUと7日間未使用の自動削除

10GBに達すると、最終アクセス日の古いものから順に退避されます。ここでの「アクセス」は保存時ではなく復元時です。作られてから一度も復元されていないキャッシュは、作成直後から退避候補の先頭に並びます。

もうひとつが7日ルールです。直近1週間アクセスされなかったキャッシュは、容量に余裕があっても削除されます。月次でしか回さないリリース用ワークフロー専用のキャッシュは、毎回ミスすると考えてください。この場合は、日次で回るワークフローと`key`の接頭辞を共有させ、`restore-keys`で拾える構成にするほうが現実的です。

### gh cache listとgh cache deleteで進める棚卸しと削除

一覧はActionsタブ配下のCachesから確認できます。フィルタ欄で`key:`に続けてキー名を書けば該当キーだけを絞り込め、ブランチのドロップダウンでスコープ別にも見られます。

CLIからの棚卸しは`gh cache list`に`--ref`と`--json id`を付けて対象を取り、`gh cache delete`で消す流れです。ワークフロー内から削除するには`actions: write`権限が要ります。壊れたキャッシュを疑ったときは、削除より`key`の第2層へ`v2`のような世代番号を挿すほうが安全でしょう。削除は全ブランチに影響しますが、世代番号なら該当ワークフローだけを切り替えられます。

## キャッシュが復元されないときに上から順に確認する4つの原因と対処

「設定したのに毎回ミスする」という相談の原因は、だいたい次の4つに収束します。頻度の高い順に並べました。

### ブランチスコープにより兄弟ブランチのキャッシュが見えない制約

ワークフローがアクセスできるのは、現在のブランチ、既定ブランチ、プルリクエストのベースブランチに作られたキャッシュだけです。子ブランチ、兄弟ブランチ、別タグのキャッシュには到達できません。

feature-Aで作ったキャッシュはfeature-Bから見えない、というのがこの制約の実害です。ブランチ単位ではなく反映先単位でスコープを分けたい値は、キャッシュではなく[GitHub ActionsのEnvironments｜承認ゲートと環境別シークレットの設計](https://www.issoh.co.jp/tech/details/16935/)で扱う環境側が受け持ちます。ブランチを切るたびに初回はフルミスします。対処は既定ブランチで先にキャッシュを作らせること。`main`宛てのプッシュでキャッシュ生成ジョブを回しておけば、以降どのブランチからも`restore-keys`経由で拾えます。プルリクエストはフォークからのものを含めてベースブランチのキャッシュを読めるため、この一手で初回ミスの大半は消えます。

### keyにハッシュを入れ忘れて古い依存が復元され続ける事故の型

逆向きの失敗もあります。`key`を`Linux-node`のような固定文字列だけで組むと、完全一致が毎回成立してしまう。キャッシュは不変なので新しい内容は保存されず、初回に作られた古い依存が半永久的に復元され続けます。

症状は独特です。ローカルでは通るテストがCIだけ落ちる、依存を上げたのに古いバージョンで動いている、といった形で出ます。なお[actによるローカル実行](https://www.issoh.co.jp/tech/details/16947/)は内蔵のキャッシュサーバを使うため、手元で復元できてもGitHub側のヒット可否は判定できません。ログの`Cache restored from key:`行に現れるハッシュが、ロックファイルの更新へ追随しているかを確かめてください。追随していなければ`key`の設計ミスです。取得段階から疑う必要が出た場合、リポジトリ取得の挙動は[actions/checkoutとは｜v7の既定変更・主要入力と浅いクローンの壊れ方](https://www.issoh.co.jp/tech/details/16927/)で整理しています。

### 毎分200アップロードと1500ダウンロードの上限に触れる場合

キャッシュサービスにはリポジトリ単位のレート制限があり、公式ドキュメントは毎分200アップロード、毎分1500ダウンロードと記載しています。数十ジョブが同時に保存する大きなマトリックスでは、この上限に触れて一部の保存が落ちます。

マトリックスの全セルが同一の依存キャッシュを保存する設計になっていないか確認してください。同時に走るジョブ数そのものを絞る方向なら、[GitHub Actionsのconcurrency｜groupの設計とcancel-in-progress・queueの選び方](https://www.issoh.co.jp/tech/details/16945/)のグループ設計も併用できます。依存の取得は代表1ジョブへ寄せ、残りは復元専用の`actions/cache/restore`にする。これだけでアップロード回数はセル数分の1です。ジョブ数そのものを絞る方向なら、[GitHub Actionsの環境変数｜env・vars・secretsの使い分けと受け渡し](https://www.issoh.co.jp/tech/details/16921/)で扱っている`vars`によるマトリックス制御が使えます。

### フォークからのPRが読み取り専用アクセスで保存に失敗する挙動

フォーク元から出されたプルリクエストのワークフローは、キャッシュへの読み取り専用アクセスになります。復元はできても保存はできません。しかもこの保存失敗はステップもジョブも落とさないため、ログを読まない限り気づけない無音の故障です。

公開リポジトリで「コントリビューターのPRだけ極端に遅い」という現象は、ほぼこれです。`v6.1.0`では読み取り専用トークンによる書き込みエラーの扱いが修正され、冗長だった警告もデバッグログへ降格されました。`v5`系のまま止めているなら、まずここを上げてログを読み直してください。

## actions/cacheを入れる条件と入れないほうが速い場面の線引き

ここは条件を付けて言い切ります。キャッシュは無条件の高速化手段ではありません。

### 復元と展開にかかる秒数が再取得を上回る小規模依存での見送り判断

導入しないほうがよい条件は明確です。依存の取得が30秒未満で済むジョブには入れないでください。復元はネットワーク経由のダウンロードと展開を伴うため、数百MBの`node_modules`相当なら20秒前後かかります。取得が15秒のジョブに20秒の復元を足せば、単純に遅くなります。

判断は実測で行います。キャッシュなしで依存取得ステップの所要時間をログから拾い、キャッシュありの回の`Cache restored`までの秒数と比べる。差が10秒未満なら外すほうが構成もシンプルです。ジョブ数の多いパイプラインでは、この10秒が全セル分積み上がる点も併せて見てください。

### Dockerレイヤーキャッシュを別系統としてbuildxへ寄せる

コンテナイメージのビルドを`actions/cache`で扱うのは避けてください。`path`にレイヤーの保存先を指定する構成も動きはしますが、保存と復元でイメージ全体を往復させるため、レイヤー単位の差分という利点が消えます。

この用途はBuildKit側の仕組みへ寄せるのが正解です。`cache-from`と`cache-to`でレジストリやGitHub Actionsのキャッシュバックエンドを指定すれば、変更のあったレイヤーだけが転送されます。仕組みと採用判断は[BuildKitとは｜Dockerの新ビルドバックエンドの仕組みとキャッシュ・マルチアーキの解説](https://www.issoh.co.jp/tech/details/15701/)にまとめてあるので、コンテナビルドの短縮が目的ならそちらから読むほうが早く片付きます。

GitHub Actions上で`cache-from`と`cache-to`をどう書き分けるかは、[GitHub ActionsでDockerイメージをビルドする｜push先とキャッシュ・マルチアーキの設計](https://www.issoh.co.jp/tech/details/16943/)で手順まで扱っています。

### v6系への更新とNode 24要件がセルフホストに課す前提条件

バージョン固定の判断も条件付きで示します。`v5.0.0`以降はNode.js 24ランタイムで動き、Actionsランナー2.327.1以上を要求します。GitHubホストランナーなら意識は不要ですが、セルフホストランナーを長期間更新していない現場では、この要件を満たさずアクションが起動しません。ランナーを上げられないなら`v4`系に留め、上げられるなら`v6.1.0`へ進む二択です。

`v6.0.0`は2026年6月23日、`v6.1.0`は同月26日の公開で、内部的にはESMへの移行が主な変更点でした。読み取り専用トークンでの書き込みエラー対応だけが要るなら、同日公開の`v5.1.0`にバックポートされています。パイプライン全体の設計から見直す段階なら、[CI/CDとは？仕組み・パイプライン・導入すべき企業の判断基準](https://www.issoh.co.jp/column/details/13002/)で前提をそろえ直すほうが順序として正しい。既存のCI基盤の棚卸しから引き取ってほしい場合は、[保守運用・内製化支援](https://www.issoh.co.jp/service/system/maintenance/)でワークフローの実行時間とストレージ枠の両面から対応しています。

## よくある質問

設定時に判断が分かれやすい点をまとめました。

### actions/cacheのバージョンはどれを指定すればよいですか？

2026年8月25日時点の最新メジャーは`v6.1.0`です。GitHubホストランナーを使っているなら`v6`系で問題ありません。セルフホストランナーの場合は、`v5.0.0`以降がNode.js 24ランタイムとランナー2.327.1以上を要求する点を先に確認してください。ランナーを上げられないなら`v4`系に留めます。

### キャッシュが10GBを超えると古いものから消えますか？

消えます。リポジトリあたり既定10GBに達すると、最終アクセス日が古い順にLRUで退避される仕組みです。ここでのアクセスは復元を指すので、保存されただけで一度も使われていないキャッシュが最初に削られます。加えて、7日間アクセスのないキャッシュは容量に余裕があっても消えます。

### mainブランチで作ったキャッシュはPRから復元できますか？

できます。ワークフローは現在のブランチ、既定ブランチ、プルリクエストのベースブランチのキャッシュへアクセスできる仕様で、フォークからのプルリクエストも同様です。ただしフォークからのPRは読み取り専用アクセスになるため、復元はできても保存はできません。兄弟ブランチや別タグのキャッシュには到達できない点も併せて押さえてください。

### 同じkeyでキャッシュを更新することはできますか？

できません。キャッシュは作成後に不変で、同じ`key`で保存しようとすると既存が優先され、新しい内容は破棄されます。更新を反映させたいなら`key`を変える以外に手段はありません。実務では`hashFiles`でロックファイルのハッシュを末尾へ入れ、依存が変わったときだけ新しい`key`になるよう組みます。

### setup-nodeのcacheだけで足りますか、actions/cacheも要りますか？

依存パッケージのキャッシュだけなら`setup-node`の`cache`入力で足ります。組み込みキャッシュは`setup-python`、`setup-java`、`setup-ruby`、`setup-go`、`setup-dotnet`にもあります。`actions/cache`が要るのは、ビルド生成物やブラウザバイナリなど組み込み対象外のディレクトリを持ち回りたいときです。

## 関連記事

- [GitHub Actionsの料金｜2026年改定後の分単価・無料枠とコスト削減の判断基準](https://www.issoh.co.jp/tech/details/16698/)：キャッシュ枠を広げたときのストレージ課金を実額で確認できます。
- [GitHub Actionsのruns-on｜ラベル指定の記法と-latest更新・arm64への備え](https://www.issoh.co.jp/tech/details/16929/)：OSごとにキャッシュを分ける前提となるランナー選択の設計です。
- [actions/checkoutとは｜v7の既定変更・主要入力と浅いクローンの壊れ方](https://www.issoh.co.jp/tech/details/16927/)：キャッシュ前段のリポジトリ取得で起きる不整合を切り分けられます。
- [BuildKitとは｜Dockerの新ビルドバックエンドの仕組みとキャッシュ・マルチアーキの解説](https://www.issoh.co.jp/tech/details/15701/)：コンテナのレイヤーキャッシュはこちらの系統で扱います。
- [GitHub Actionsとは？できること・使い方とCI/CD自動化の解説記事](https://www.issoh.co.jp/column/details/3005/)：ジョブとステップの基本構造から確認したい場合の入口です。

---

出典: [GitHub Actionsのキャッシュ｜actions/cacheのkey設計と復元されない原因の切り分け](<https://www.issoh.co.jp/tech/details/16933/>)（株式会社一創）
