GitHub Actionsのキャッシュとは、依存パッケージなど毎回ダウンロードするファイルを保存し、次の実行で復元して時間を短くする仕組みです。matrix(strategy.matrix)とは、Nodeのバージョンや OS の組み合わせを1つのジョブ定義から並列に展開する仕組みです。 この2つは一緒に使う場面が多く、キャッシュのキーに matrix の値を入れるかどうかで「効く・効かない」が分かれます。

この記事では、2026年10月時点の最新版(actions/cache v6、setup-node v7、setup-python v7、setup-java v6、checkout v7)を公式リポジトリのタグで確認し、コピペで使える YAML、キー設計、保存条件とスコープ、「効かない原因一覧」を表で整理しました。掲載した YAML はすべて actionlint 1.7.12 で静的チェックし、その出力を載せています。 検証環境では GitHub Actions 本体を動かせないため、ワークフローの実行ログは載せていません(未実行)。CI/CD の全体像は別記事「CI/CDとは|GitHub Actionsで最初のパイプラインを作るまで」で扱っています。

GitHub Actionsのキャッシュとmatrixの早見表

やりたいこと 書き方 ポイント
npm の依存をキャッシュ actions/setup-node@v7 に cache: npm node_modules ではなく npm のグローバルキャッシュを保存
pip の依存をキャッシュ actions/setup-python@v7 に cache: pip 対応は pip / pipenv / poetry
Gradle / Maven をキャッシュ actions/setup-java@v6 に cache: gradle 対応は maven / gradle / sbt
任意のフォルダをキャッシュ actions/cache@v6 の path と key キーは最大512文字
キーをロックファイルで変える hashFiles('**/package-lock.json') 一致ファイルが無いと空文字
OS ごとに分ける キーに runner.os を入れる Linux / Windows / macOS
matrix ごとに分ける キーに matrix.node などを入れる 中身が変わる軸だけ入れる
完全一致が無いとき近いものを使う restore-keys を複数行で 上から順に前方一致、複数なら新しい方
完全一致したか知る steps.<id>.outputs.cache-hit 完全一致で 'true'
失敗しても保存したい actions/cache/restore と actions/cache/save に分ける save に if: always()
複数バージョンで並列テスト strategy.matrix 1回の実行で最大256ジョブ
特定の組み合わせを外す matrix.exclude include より先に処理
組み合わせに値を足す / 追加する matrix.include 既存に足せなければ新しい組み合わせ
1つ失敗しても他を続ける fail-fast: false 既定は true
同時実行数を抑える max-parallel: 2 ランナーの取り合いを避ける
matrix を前のジョブで作る matrix: ${{ fromJSON(needs.plan.outputs.matrix) }} 出力は JSON 文字列で渡す
キャッシュを一覧・削除 gh cache list / gh cache delete <key> リポジトリの Actions → Caches でも可

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「GitHub Actionsのキャッシュとmatrix|コピペ用YAMLとキー設計」

2026年10月時点の公式アクションのバージョン

まず、使う公式アクションのメジャーバージョンを揃えます。 下表は、各リポジトリのタグ(git ls-remote --tags)と README で確認した内容です。

アクション 最新メジャー 主な変更点(公式の記載)
actions/cache v6(v6.1.0) v6 で内部を ESM に移行。v5 で Node.js 24 ランタイム、セルフホストランナーは 2.327.1 以上が必要。v4 でキャッシュサービス v2 API に対応
actions/setup-node v7(v7.0.0) v6 から、package.json の packageManager(または devEngines.packageManager)が npm のとき、npm のキャッシュが自動で有効
actions/setup-python v7(v7.0.0) v6 で Node.js 24 ランタイム、v7 は内部の ESM 移行(使い方の変更なし)
actions/setup-java v6(v6.0.1) Node.js 24 ランタイム。server-username などの入力名が ...-env-var に変更
actions/checkout v7(v7.0.1) ―

セルフホストランナーを使っている場合は、ランナー 2.327.1 以上が前提になります。 GitHub ホストランナー(ubuntu-latest など)なら意識しなくて大丈夫です。

まずはsetupアクションのcacheオプションを使う

依存パッケージのキャッシュは、setup 系アクションの cache 入力を1行足すのがいちばん簡単です。 内部で actions/cache を使い、キーも自動で作ってくれます。

アクション cache の値 キーに使うファイル(既定) 保存されるもの
setup-node@v7 npm / yarn / pnpm package-lock.json・yarn.lock・pnpm-lock.yaml 各パッケージマネージャーのグローバルキャッシュ(node_modules は対象外)
setup-python@v7 pip / pipenv / poetry requirements.txt・Pipfile.lock・poetry.lock など pip などのキャッシュフォルダ
setup-java@v6 maven / gradle / sbt Maven は **/pom.xml など、Gradle は **/*.gradle*・gradle-wrapper.properties など ~/.m2/repository、~/.gradle/caches など

ロックファイルが別の場所にあるときや、複数ファイルを対象にしたいときは cache-dependency-path で指定します。

Node.js × OS の matrix(setup-node v7)

# .github/workflows/node-matrix.yml
name: node-matrix
on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      max-parallel: 4
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node: [20, 22, 24]
        exclude:
          - os: macos-latest
            node: 20
        include:
          - os: ubuntu-latest
            node: 24
            coverage: true
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: ${{ matrix.node }}
          cache: npm
      - run: npm ci
      - run: npm test
      - if: ${{ matrix.coverage }}
        run: npm run coverage

このワークフローは 3 OS × 3 バージョン = 9 通りから exclude で1つ外し、8 ジョブになります。 include の {os: ubuntu-latest, node: 24, coverage: true} は、既存の組み合わせ(ubuntu-latest × 24)の値を書き換えずに coverage を足せるので、新しいジョブは増えず、その1ジョブにだけ coverage: true が付きます。 前提として、リポジトリに package-lock.json と、test・coverage の npm スクリプトが必要です。

Python のバージョン matrix(setup-python v7)

# .github/workflows/python-matrix.yml
name: python-matrix
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12", "3.13", "3.14"]
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-python@v7
        with:
          python-version: ${{ matrix.python-version }}
          cache: pip
          cache-dependency-path: |
            requirements.txt
            requirements-dev.txt
      - run: pip install -r requirements.txt -r requirements-dev.txt
      - run: pytest -q

バージョンは必ず文字列("3.11")で書きます。 3.10 を数値で書くと YAML では 3.1 と解釈されるため、Python のバージョンは引用符で囲むのが定番です。

Java のバージョン matrix(setup-java v6 + Gradle)

# .github/workflows/java-matrix.yml
name: java-matrix
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        java: ["17", "21", "25"]
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: ${{ matrix.java }}
          cache: gradle
      - run: ./gradlew build --no-daemon

Maven の場合は cache: maven に変え、run: mvn -B verify にします。distribution は必須入力なので忘れずに書きます。

actions/cache v6でキーを自分で設計する

setup 系で対応していないフォルダ(ビルド成果物、ツールのキャッシュなど)や、キーを細かく制御したいときは actions/cache を直接使います。 次の例は、npm のキャッシュを OS と Node のバージョンごとに分け、近いキャッシュへのフォールバックも用意したものです。

# .github/workflows/cache-manual.yml
name: cache-manual
on: [push, pull_request]

jobs:
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        node: [22, 24]
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: ${{ matrix.node }}
      - name: npm のキャッシュフォルダを取得
        id: npm-cache-dir
        shell: bash
        run: echo "dir=$(npm config get cache)" >> "$GITHUB_OUTPUT"
      - name: キャッシュを復元(成功時は保存)
        id: cache
        uses: actions/cache@v6
        with:
          path: ${{ steps.npm-cache-dir.outputs.dir }}
          key: npm-${{ runner.os }}-node${{ matrix.node }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            npm-${{ runner.os }}-node${{ matrix.node }}-
            npm-${{ runner.os }}-
      - run: echo "cache-hit=${{ steps.cache.outputs.cache-hit }}"
      - run: npm ci
      - run: npm test

actions/cache の入力と出力

名前 種類 意味
path 入力(必須) 保存・復元するファイルやフォルダ。複数行・ワイルドカード可
key 入力(必須) キャッシュの名前。最大512文字
restore-keys 入力 完全一致が無いときに前方一致で探す候補(上から順)
enableCrossOsArchive 入力 Windows と他 OS の間で復元を許可(既定 false)
fail-on-cache-miss 入力 完全一致が無ければジョブを失敗にする(既定 false)
lookup-only 入力 ダウンロードせず、存在だけ確認(既定 false)
cache-hit 出力 key と完全一致したら 'true'

cache-hit は「完全一致したか」だけを表します。 restore-keys で近いキャッシュが復元された場合は 'true' になりません。そのため「cache-hit が true なら npm ci を飛ばす」という書き方は、node_modules そのものをキャッシュしている場合に限って使うのが安全です。

キー設計の早見表

キーの部品 例 入れる理由 入れないとどうなるか
用途の接頭辞 npm- / gradle- 複数キャッシュの区別、restore-keys の起点 別用途のキャッシュを前方一致で拾う
runner.os Linux / Windows / macOS OS でパスやバイナリが違う 別 OS 用の中身を拾う(既定では Windows と他 OS の間は復元されない)
runner.arch X64 / ARM64 ネイティブモジュールなどアーキテクチャ依存の中身 ARM と x64 で中身が混ざる
matrix の値 node22 / py3.13 バージョンで中身が変わるもの(ビルド成果物、venv など) 別バージョン用の成果物を拾う
hashFiles(...) ロックファイルのハッシュ 依存が変わったら新しいキーにする 依存を更新しても古いキャッシュのまま
日付・週番号 2026-w40 定期的に作り直したいとき 同じキーが残り続ける

判断の基準は「キャッシュの中身がその値で変わるか」です。 npm のダウンロードキャッシュは Node のバージョンで中身がほぼ変わらないので、Node のバージョンをキーから外して共有する設計もありです。一方、node_modules、Python の venv、ビルド成果物はバージョンで中身が変わるので、matrix の値をキーに入れます。

hashFiles と restore-keys の動きを手元で再現する

GitHub Actions 本体は動かせないため、hashFiles と同じ考え方(一致した各ファイルの SHA-256 を順に1つのハッシュへまとめる)と、restore-keys の探し方(公式ドキュメントの記載どおり)を Python で再現しました。GitHub の実装そのものではなく、動きを理解するためのシミュレーションです。

# keysim.py(Python 3.11)
import hashlib, pathlib

def hash_files(*patterns):
    # 各ファイルのSHA-256を順に1つのSHA-256へ流し込む(hashFiles と同じ考え方)
    files = sorted({p for pat in patterns for p in pathlib.Path('.').glob(pat) if p.is_file()})
    if not files:
        return ''  # 一致するファイルが無いと hashFiles は空文字を返す
    total = hashlib.sha256()
    for f in files:
        total.update(hashlib.sha256(f.read_bytes()).digest())
    return total.hexdigest()

def restore(key, restore_keys, caches):
    # 1) key と完全一致 2) restore-keys を上から順に前方一致、複数あれば新しいもの
    if key in caches:
        return key, 'true'
    for rk in restore_keys:
        hits = [k for k in caches if k.startswith(rk)]
        if hits:
            return max(hits, key=lambda k: caches[k]), 'false'
    return None, ''

pathlib.Path('package-lock.json').write_text('{"lockfileVersion":3,"packages":{"":{"name":"todo"}}}\n')
h1 = hash_files('**/package-lock.json')
for os_ in ['Linux', 'Windows']:
    for node in [22, 24]:
        print(f'npm-{os_}-node{node}-{h1[:12]}...')

caches = {f'npm-Linux-node24-{h1}': 1}  # 値は作成順(大きいほど新しい)
pathlib.Path('package-lock.json').write_text('{"lockfileVersion":3,"packages":{"":{"name":"todo","version":"1.0.1"}}}\n')
h2 = hash_files('**/package-lock.json')
print('lockfile 変更前', h1[:12], '→ 変更後', h2[:12])

caches[f'npm-Linux-node22-{h1}'] = 2
key = f'npm-Linux-node24-{h2}'
print('restore:', restore(key, ['npm-Linux-node24-', 'npm-Linux-'], caches))
print('lockfile無し:', repr(hash_files('**/yarn.lock')))

実行結果(python3 keysim.py):

npm-Linux-node22-7adeafc19171...
npm-Linux-node24-7adeafc19171...
npm-Windows-node22-7adeafc19171...
npm-Windows-node24-7adeafc19171...
lockfile 変更前 7adeafc19171 → 変更後 17bfca6f5cba
restore: ('npm-Linux-node24-7adeafc19171a77d8929ecde9df2be249a95b4ea4f62d00050b2bf7a86b41f63', 'false')
lockfile無し: ''

読み取れるポイントは3つです。

  • matrix の4ジョブはそれぞれ別のキーになるので、同時に動いても保存がぶつかりません。
  • ロックファイルが変わるとキーが変わり、完全一致はしません。 そこで restore-keys の1行目 npm-Linux-node24- が前方一致し、変更前のキャッシュが復元されます(cache-hit は 'false')。2行目の npm-Linux- には node22 用もありますが、1行目で見つかった時点で探索は終わるため、より近い node24 用が選ばれます。
  • ロックファイルが無いと hashFiles は空文字になり、キーが npm-Linux-node24- のように固定されます。依存を変えてもキーが変わらないので、ロックファイルをコミットしておくことが大切です。

失敗しても保存したい:restore と save に分ける

actions/cache は、ジョブが成功したときだけ、ジョブの最後(post ステップ)でキャッシュを保存します。 テストが落ちても依存やビルドのキャッシュは残したい場合は、actions/cache/restore と actions/cache/save に分けます。

# .github/workflows/cache-restore-save.yml
name: cache-restore-save
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - name: 復元だけ行う
        id: restore
        uses: actions/cache/restore@v6
        with:
          path: .build-cache
          key: build-${{ runner.os }}-${{ hashFiles('src/**', 'build.config.json') }}
          restore-keys: |
            build-${{ runner.os }}-
      - run: ./scripts/build.sh
      - name: テストが失敗しても保存する
        if: ${{ always() && steps.restore.outputs.cache-hit != 'true' }}
        uses: actions/cache/save@v6
        with:
          path: .build-cache
          key: ${{ steps.restore.outputs.cache-primary-key }}

cache-hit != 'true' の条件を付けているのは、キャッシュが「同じキーでは上書きできない」からです。 完全一致で復元できたなら保存する必要がなく、保存しようとしても既に同じキーがあるため作成されません。cache-primary-key は restore アクションが出力する「今回の key」で、save 側で同じキーを使い回せます。

キャッシュの保存条件・上限・スコープの表

キャッシュは「いつ保存され、誰が使えて、いつ消えるか」を押さえると、効かない理由がすぐわかります。

項目 ルール(公式ドキュメントの記載) 使い方のヒント
保存のタイミング ジョブが成功すると post ステップで保存 失敗時も残すなら restore / save に分ける
上書き 同じキーのキャッシュは作り直されない 中身を更新したいならキーを変える
キーの長さ 最大512文字。超えるとアクションが失敗 長い値はハッシュにして入れる
リポジトリの合計 既定で10GBまで 上限を超えると最後に使われた日が古い順に削除
使われない期間 7日以上アクセスされないと削除 定期実行で温めておく方法もある
上限の変更 リポジトリ・組織の設定で引き上げ可能 詳細は公式の dependency caching のページ

ブランチ間でどこまで共有できるか

実行したブランチ 復元できるキャッシュ
main(既定ブランチ) main で作ったもの
feature-a feature-a で作ったもの、main で作ったもの
feature-a から切った feature-b feature-b、main で作ったもの(feature-a のものは使えない)
main への pull request PR の merge ref(refs/pull/<番号>/merge)、ベースブランチ(main)で作ったもの
PR で作られたキャッシュ 同じ PR の再実行からしか使えない

ここから導ける設計のコツは「main への push でもワークフローを動かしてキャッシュを作っておく」ことです。 on: pull_request だけだと、各 PR が自分専用のキャッシュを作るだけで、他の PR には共有されません。上の node-matrix.yml で push: branches: [main] を入れているのはこのためです。

キャッシュを一覧・削除する

# GitHub CLI(gh)でキャッシュを確認・削除する
gh cache list --limit 20
gh cache delete npm-Linux-node24-7adeafc19171a77d8929ecde9df2be249a95b4ea4f62d00050b2bf7a86b41f63
gh cache delete --all

ブラウザでは、リポジトリの「Actions」タブ → 左メニューの「Caches」から一覧・削除できます。 中身がおかしいキャッシュを消して作り直したいとき、キーの接頭辞を v2-npm-... のように変える方法も手軽です。

strategy matrixの早見表

キー 書き方 動き
matrix.<軸> node: [22, 24] 軸の値の直積でジョブを作る
exclude - {os: macos-latest, node: 20} 一致する組み合わせを外す(include より先に処理)
include - {os: ubuntu-latest, node: 24, coverage: true} 既存の値を書き換えずに足せる組み合わせに追加、足せなければ新しい組み合わせ
fail-fast fail-fast: false 既定 true。1つ失敗すると残りをキャンセル
max-parallel max-parallel: 2 同時に動かすジョブ数の上限
continue-on-error continue-on-error: ${{ matrix.experimental }} 特定の組み合わせだけ失敗を許す(ジョブ側に書く)
動的 matrix ${{ fromJSON(needs.plan.outputs.matrix) }} 前のジョブが作った JSON で展開
参照 ${{ matrix.node }} ステップや runs-on、name で使える
上限 ― 1回のワークフロー実行で最大256ジョブ

include の動きを表で確認する

os: [ubuntu-latest, windows-latest]、node: [22, 24] に対して、include を書いたときの結果です。

include に書いた内容 既存の値を書き換えるか 結果
{experimental: false} 書き換えない 4ジョブすべてに experimental: false が付く
{os: windows-latest, shell: pwsh} 書き換えない windows の2ジョブに shell: pwsh が付く
{os: ubuntu-latest, node: 26, experimental: true} node を書き換える 新しい5ジョブ目として追加

「値を足すだけなら既存に付く、軸の値を変えるなら新しいジョブ」と覚えると迷いません。 試験的なバージョンだけ失敗を許すなら、experimental: [false] を軸に置き、include で {node: 26, experimental: true} を追加して、ジョブに continue-on-error: ${{ matrix.experimental }} を書きます。

動的 matrix(fromJSON)で、フォルダごとにジョブを作る

monorepo の packages/ 以下のフォルダ数に合わせてジョブを作りたいときは、前のジョブで JSON を作り、fromJSON で渡します。

# .github/workflows/dynamic-matrix.yml
name: dynamic-matrix
on:
  workflow_dispatch:
  push:
    branches: [main]

jobs:
  plan:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set.outputs.matrix }}
    steps:
      - uses: actions/checkout@v7
      - id: set
        run: |
          dirs=$(find packages -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort | jq -R . | jq -cs .)
          echo "matrix={\"pkg\":${dirs}}" >> "$GITHUB_OUTPUT"

  test:
    needs: plan
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
          cache: npm
          cache-dependency-path: packages/${{ matrix.pkg }}/package-lock.json
      - run: npm ci
        working-directory: packages/${{ matrix.pkg }}
      - run: npm test
        working-directory: packages/${{ matrix.pkg }}

plan ジョブのシェル部分を手元(bash、packages/api・web・worker を作成)で動かした結果です。

matrix={"pkg":["api","web","worker"]}

この JSON が fromJSON で {pkg: [api, web, worker]} の matrix になり、3ジョブに展開されます。 パッケージごとに cache-dependency-path を変えているので、キャッシュのキーも自動で分かれます。

actionlintで全YAMLを静的チェックした結果

ワークフローは push してから気づく誤りが多いので、actionlint で事前に確認しておくと手戻りが減ります。 actionlint は PyPI の actionlint-py からも入れられます。

python3 -m venv .venv && . .venv/bin/activate
pip install actionlint-py
actionlint -version
actionlint -no-color

最初の実行結果(バージョン表示と、この記事の YAML 6本のチェック):

1.7.12
installed by downloading from release page
built with go1.26.1 compiler for linux/amd64
.github/workflows/dynamic-matrix.yml:15:9: shellcheck reported issue in this script: SC2011:warning:1:8: Use 'find .. -print0 | xargs -0 ..' or 'find .. -exec .. +' to allow non-alphanumeric filenames [shellcheck]
   |
15 |         run: |
   |         ^~~~

最初の版では ls -d packages/*/ | xargs -n1 basename と書いていたため、shellcheck 連携の警告が出ました。 上に載せた find ... -printf '%f\n' に直したあとの再実行では、6本とも指摘なし(終了コード 0)になりました。

よくある書き間違いを actionlint で見つける

わざと間違えた YAML でチェックした結果です(-oneline 相当に、コード枠の表示を省いて載せています)。

# .github/workflows/broken.yml(わざと間違えた例)
name: broken
on: [push]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest]
        node: [22, 24]
        exclude:
          - node: 20
    steps:
      - uses: actions/setup-node@v7
        with:
          node-version: ${{ matrix.nodes }}
          cache: npm
      - uses: actions/cache@v6
        with:
          path: ~/.npm
          key: npm-${{ hashFile('package-lock.json') }}
          restore-key: npm-
.github/workflows/broken.yml:12:19: value "20" in "exclude" does not match in matrix "node" combinations. possible values are "22", "24" [matrix]
.github/workflows/broken.yml:16:29: property "nodes" is not defined in object type {node: number; os: string} [expression]
.github/workflows/broken.yml:21:24: undefined function "hashFile". available functions are "always", "cancelled", "case", "contains", "endswith", "failure", "format", "fromjson", "hashfiles", "join", "startswith", "success", "tojson" [expression]
指摘 原因 直し方
exclude の値が matrix に無い 軸に無い 20 を指定 軸の値と揃える
matrix.nodes が未定義 軸名のタイプミス matrix.node
hashFile が未定義 関数名のタイプミス hashFiles

restore-key(正しくは restore-keys)は、actions/cache@v6 では指摘されませんでした。 同じファイルを actions/cache@v4 に変えて実行すると、次のように入力名の誤りまで検出されます。actionlint 1.7.12 が入力名を知っているのは内部のデータにあるバージョンまでのため、最新メジャーに上げた直後は、入力名を README で目視確認するのがおすすめです。

input "restore-key" is not defined in action "actions/cache@v4". available inputs are "enableCrossOsArchive", "fail-on-cache-miss", "key", "lookup-only", "path", "restore-keys", "save-always", "upload-chunk-size" [action]

キャッシュが効かない原因一覧

症状 原因 直し方
毎回 cache-hit が空・false キーに github.sha や日時など毎回変わる値が入っている 中身が変わるときだけ変わる値(ロックファイルのハッシュ)にする
依存を変えても古いまま ロックファイルが無く hashFiles が空文字 ロックファイルをコミットする
Windows だけ遅い / 中身がおかしい キーに runner.os が無く別 OS 用を拾う、または既定で OS をまたいで復元されない キーに runner.os を入れる
PR ごとに毎回ミス PR のキャッシュは merge ref 専用で、他の PR から使えない main への push でもワークフローを動かす
テストが落ちた回のキャッシュが残らない 保存はジョブ成功時のみ restore / save に分け if: always()
同じキーで更新されない キャッシュは上書きされない キーを変える(接頭辞に v2- など)
大きなキャッシュが消えている リポジトリ合計の上限超えで古い順に削除 不要なキャッシュを gh cache delete、対象を絞る
path を変えたら当たらない キャッシュは path と圧縮方式も含めて区別される 復元側と保存側で path を揃える
setup-node の cache でエラー ロックファイルが見つからない(サブフォルダにある) cache-dependency-path で場所を指定

よくある質問

Q. setup-node の cache: npm と actions/cache は、どちらを使えばよいですか? A. 依存パッケージだけなら setup 系の cache 入力がおすすめです。 キーの作り方が公式で保たれ、YAML も短くなります。ビルド成果物や独自ツールのキャッシュ、キーを細かく制御したい場合は actions/cache を使います。

Q. node_modules をそのままキャッシュしてもよいですか? A. 可能ですが、OS と Node のバージョンをキーに必ず入れてください。 ネイティブモジュールを含む node_modules は環境で中身が変わります。setup-node はグローバルキャッシュを保存し npm ci で組み立てる方式なので、迷ったらそちらが扱いやすいです。

Q. matrix のジョブごとにキャッシュを分けるべきですか? A. 中身がその軸で変わるなら分け、変わらないなら共有します。 npm のダウンロードキャッシュは OS で分け、Node のバージョンでは分けない、venv やビルド成果物は Python・Java のバージョンでも分ける、が目安です。

Q. PR から main のキャッシュは使えますか? A. 使えます。 PR はベースブランチ(main)のキャッシュを復元できます。逆に PR で作られたキャッシュは、その PR の再実行でしか使えません。

まとめ

  • 最新は actions/cache v6、setup-node v7、setup-python v7、setup-java v6(2026年10月時点)。依存のキャッシュは setup 系の cache 入力が手軽です。
  • キーは「接頭辞+runner.os+中身が変わる matrix 値+hashFiles」で組み、restore-keys は上から順に前方一致、複数なら新しいものが選ばれます。
  • 保存はジョブ成功時のみ、同じキーは上書きされないので、失敗時も残すなら restore / save に分けます。
  • リポジトリ合計は既定10GB、7日使われないと削除。PR のキャッシュは同じ PR 専用なので、main への push でも動かして共有元を作ります。
  • matrix は exclude が先、include は「足せるなら既存に、変えるなら新規」。fail-fast: false と max-parallel で失敗時と同時実行数を調整します。
  • 動的 matrix は JSON を出力して fromJSON で受け取ります。
  • actionlint で事前チェックすると、軸名や関数名のタイプミスを push 前に見つけられます。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「GitHub Actionsのキャッシュとmatrix|コピペ用YAMLとキー設計」

株式会社RJCでは、GitHub Actionsのキャッシュやmatrixの設計を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?