# GitHub Actions で APT 依存をキャッシュしてワークフローを高速化する

  GitHub Actions のランナーはジョブ実行のたびにクリーンな状態で起動します。ビルドの再現性を保証するための設計ですが、「毎回クリーン」ということはシステム依存もゼロからそろえ直すことを意味し、ブラウザを含むプロジェクトでは実行時間に直接影響します。

Vercel Labs が開発した [agent-browser](https://github.com/vercel-labs/agent-browser) は AI エージェント向けのブラウザ操作ライブラリです。実際の Chrome を動かすため、Ubuntu ランナーではシステム依存パッケージも合わせてインストールが必要です。GitHub Actions 上で agent-browser を使い始めると、ワークフロー実行ごとにこのインストールが発生し、実行時間が膨らんでいきます。

「ランナーが毎回クリーンなのだからシステム依存のキャッシュは無理」と思い込んでいましたが、調べると APT[^apt] パッケージも `.deb` ファイル単位でキャッシュできます。この記事では、その発見に至るまでの各アプローチを順番に検証し、最終的に CI 実行時間を 50〜60% 削減した構成を紹介します。

## ベースラインを計測する

まずキャッシュなしの CI で実行時間を計測しました。

| 項目 | 内容 |
|---|---|
| ランナー | `ubuntu-latest`(Ubuntu 24.04) |
| agent-browser | `npm install -g agent-browser`(グローバルインストール) |
| Chrome | Chrome for Testing 148.0.7778.97(175MB) |
| 検証サイト | https://claude-code-log.com (筆者開発のサイト) |
| 計測方法 | 各アプローチを複数回(4〜9回)実行して実行時間の最小〜最大を記録 |

```yaml
name: ベースライン計測(キャッシュなし)

permissions: {}

on:
  push:
    branches:
      - main

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  baseline:
    name: キャッシュなし
    timeout-minutes: 15
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false

      - name: agent-browser をインストール
        run: npm install -g agent-browser

      - name: Chrome とシステム依存をインストール
        run: agent-browser install --with-deps

      - name: ブラウザ操作(open → snapshot → close)
        run: |
          agent-browser batch \
            "open https://claude-code-log.com" \
            "snapshot -i" \
            "close"
```

計測結果は合計 43 秒で、 `agent-browser install --with-deps` が 33 秒を占めています。

## ボトルネックを深掘りする

`--with-deps` が内部で実行する `apt-get install` のログを確認してみましょう。

```
0 upgraded, 2 newly installed, 0 to remove and 53 not upgraded.
The following NEW packages will be installed:
  fonts-freefont-ttf fonts-noto-cjk
Need to get 66.9 MB of archives.
```

`--with-deps` は内部で 35 パッケージのリストを `apt-get` に渡しますが、実際に新規インストールされたのは `fonts-freefont-ttf` と `fonts-noto-cjk` の 2 パッケージだけです。残り 33 パッケージは Ubuntu 24.04 にすでに含まれており、66.9MB のフォントデータのダウンロードが大半の時間を占めていました。

ボトルネックが特定できたので、改善アプローチを整理します。

| アプローチ | 概要 | 期待効果 |
|---|---|---|
| Chrome のみキャッシュ | `~/.agent-browser/browsers/` をキャッシュ | Chrome ダウンロード(2.5秒)をスキップ |
| 最小パッケージ手動指定 | 不足する 2 パッケージのみ `apt install` | APT インストール対象を削減 |
| APT パッケージキャッシュ | `.deb` ファイルをキャッシュし `dpkg`[^dpkg] でインストール | `apt-get update` とダウンロードをスキップ |

## 各アプローチを検証する

### Chrome バイナリのみキャッシュ

`~/.agent-browser/browsers/` を `actions/cache` でキャッシュして Chrome の再ダウンロードをスキップする方法です。このアプローチの[検証ワークフロー](https://github.com/Suntory-N-Water/github-actions-agent-browser-cache-strategy/blob/main/.github/workflows/cache-chrome.yml)はこちらです。

キャッシュヒット時のログで `✓ Chrome 148.0.7778.97 is already installed` が確認でき、Chrome のダウンロードはスキップされています。ただしシステム依存の `apt-get` は毎回実行されるため、ほとんど改善しませんでした。

| | ベースライン | キャッシュヒット(4回) |
|---|---|---|
| 合計 | 36〜45秒 | 36〜46秒 |

Chrome のダウンロードの 2.5 秒を節約しても、APT の 30 秒超は毎回実行されます。ボトルネック自体に届いていないため、ほぼ改善しませんでした。

### 最小パッケージの手動指定

`--with-deps` を使わず、実際に不足している 2 パッケージだけを直接指定する方法です。このアプローチの[検証ワークフロー](https://github.com/Suntory-N-Water/github-actions-agent-browser-cache-strategy/blob/main/.github/workflows/cache-min-packages.yml)はこちらです。

| | ベースライン | 実測範囲(5回) |
|---|---|---|
| 合計 | 36〜45秒 | 24〜30秒 |

`apt-get install` の対象が 35 パッケージから 2 パッケージに減って速くなりましたが、`apt-get update` は毎回実行されます。フォント 2 パッケージのダウンロードとインストールで約 20 秒かかるため、Chrome キャッシュの有無に関係なく毎回同じ時間がかかります。

### APT パッケージキャッシュ

`.deb` ファイルそのものをキャッシュし、ヒット時は `apt-get update` なしで `dpkg` から直接インストールする方法です。

既存ライブラリを調べると、最も使われているのが [awalsh128/cache-apt-pkgs-action](https://github.com/awalsh128/cache-apt-pkgs-action)(★346)でした。このアクションを使った[検証ワークフロー](https://github.com/Suntory-N-Water/github-actions-agent-browser-cache-strategy/blob/main/.github/workflows/cache-apt-packages.yml)はこちらです。
動作確認では最速(ヒット時 4 秒)でしたが、内部で `actions/cache@v4`(Node.js 20)を使用しています。そのため、Node.js 20 の廃止予定(2026 年 9 月 16 日)以降に動作しなくなる可能性があり、[Issue #193](https://github.com/awalsh128/cache-apt-pkgs-action/issues/193) や [PR #198](https://github.com/awalsh128/cache-apt-pkgs-action/pull/198) で議論されているものの、メンテナンスの動きが止まっています。

もう 1 つ調べたのが [gerlero/apt-install](https://github.com/gerlero/apt-install) です。このアクションを使った[検証ワークフロー](https://github.com/Suntory-N-Water/github-actions-agent-browser-cache-strategy/blob/main/.github/workflows/cache-apt-gerlero.yml)はこちらです。シェルスクリプトのみで構成された composite action[^composite] で、内部の `actions/cache@v5` は Node.js 24 を使用しているため廃止リスクはありません。ただしキャッシュヒット時も `apt-get update` を実行するため、ヒット時でも 20 秒かかりました。

| 実装 | 実測範囲(4回) | Node.js 問題 |
|---|---|---|
| `cache-apt-pkgs-action` | 13〜19秒 | Node.js 20 廃止予定 |
| gerlero | 24〜36秒 | なし |

## 自前実装で両方の問題を解決する

`gerlero/apt-install` のしくみを参考に、キャッシュヒット時は `apt-get update` をスキップする composite action を自作しました。`actions/cache/restore@v5` と `actions/cache/save@v5`(Node.js 24)のみを使用しており、外部依存はありません。

```yaml .github/actions/apt-cache/action.yml
name: "apt キャッシュインストール"
description: "apt パッケージを actions/cache@v5 でキャッシュしてインストールする(Node.js 不使用)"

inputs:
  packages:
    description: "インストールするパッケージ(スペース区切り)"
    required: true
  cache-version:
    description: "キャッシュキーの手動バスト用バージョン"
    default: "1"

runs:
  using: "composite"
  steps:
    - name: キャッシュを復元
      id: cache
      uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
      with:
        path: ~/.apt-debs
        key: apt-${{ runner.arch }}-${{ runner.os }}-v${{ inputs.cache-version }}-${{ inputs.packages }}

    - name: パッケージをダウンロード
      if: steps.cache.outputs.cache-hit != 'true'
      shell: bash
      env:
        PACKAGES: ${{ inputs.packages }}
      run: |
        sudo apt-get update -qq
        mkdir -p ~/.apt-debs
        cd ~/.apt-debs
        apt-get download $PACKAGES

    - name: キャッシュを保存
      if: steps.cache.outputs.cache-hit != 'true'
      uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
      with:
        path: ~/.apt-debs
        key: ${{ steps.cache.outputs.cache-primary-key }}

    - name: パッケージをインストール
      shell: bash
      run: sudo dpkg --install --recursive --skip-same-version ~/.apt-debs
```

この composite action を呼び出すワークフローです。

```yaml
name: 計測(apt キャッシュ / 自前実装)

permissions: {}

on:
  push:
    branches:
      - main

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  apt-cache-custom:
    name: apt キャッシュあり(自前実装)
    timeout-minutes: 15
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false

      - name: agent-browser をインストール
        run: npm install -g agent-browser

      - name: agent-browser バージョンを取得
        id: ab-version
        run: echo "version=$(agent-browser --version)" >> "$GITHUB_OUTPUT"

      - name: Chrome バイナリをキャッシュから復元
        id: chrome-cache
        uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
        with:
          path: ~/.agent-browser/browsers
          key: ${{ runner.os }}-chrome-${{ steps.ab-version.outputs.version }}

      - name: 不足パッケージをキャッシュ付きでインストール
        uses: ./.github/actions/apt-cache
        with:
          packages: fonts-freefont-ttf fonts-noto-cjk

      - name: Chrome をインストール(キャッシュミス時のみ実際にダウンロード)
        if: steps.chrome-cache.outputs.cache-hit != 'true'
        run: agent-browser install

      - name: ブラウザ操作(open → snapshot → close)
        run: |
          agent-browser batch \
            "open https://claude-code-log.com" \
            "snapshot -i" \
            "close"
```

全アプローチの比較です。

| No | アプローチ | キャッシュ状態 | 実測範囲 |
|----|------------|----------------|----------|
| 1 | ベースライン | キャッシュなし(毎回) | 36〜45秒 |
| 2 | Chrome のみ | Chrome ヒット・APT 毎回 | 36〜46秒 |
| 3 | 最小パッケージ | Chrome ヒット・APT 毎回 | 24〜30秒 |
| 4 | `cache-apt-pkgs-action` | Chrome + APT ヒット | 13〜19秒 |
| 5 | gerlero | Chrome + APT ヒット | 24〜36秒 |
| 6 | **自前実装** | **Chrome + APT ヒット** | **16〜19秒** |

自前実装は `cache-apt-pkgs-action` とほぼ同等の速度(16〜19 秒)を保ちながら廃止リスクがなく、現時点では最もバランスが良い構成です。今後 `cache-apt-pkgs-action` が Node.js 24 に対応すれば、シンプルに既存アクションへ切り替えてよいと思います。重要なのは「APT キャッシュという戦略を選ぶこと」であって、実装の選択は Node.js 対応状況という運用上の話にすぎません。

## 注意点

### Ubuntu-latest が更新された場合の影響

今回の「新規インストールが 2 パッケージだけ」という結果は Ubuntu 24.04 時点の依存構成によるものです。Ubuntu-latest が更新されて今まで含まれていたパッケージが削除された場合、Chrome が起動できずブラウザ操作ステップで落ちる可能性はゼロではありません。コアなシステムライブラリが削除されることは考えにくいので、現実的なリスクは低いですが、念頭に置いておくとよいでしょう。

インストール対象パッケージを変更した場合は `inputs.packages` が変わるためキャッシュキーが自動的に変わります。`cache-version` の手動バストが必要になるのは、同じパッケージ構成のままキャッシュを強制リセットしたい場合だけです。

### さらに短縮するには

今回のキャッシュでは `.deb` のダウンロードをスキップできましたが、キャッシュミス時の `apt-get update`(約 10 秒)は毎回発生します。さらなる短縮にはカスタムランナーイメージが有効ですが、GitHub Team / Enterprise プランが必要です。個人リポジトリでは現実的な選択肢ではないため、今回は対象外としました。

## まとめ

- `ubuntu-latest` には Chromium の依存パッケージのほとんどがすでに含まれており、追加インストールが必要なのはフォント 2 パッケージ(`fonts-freefont-ttf` と `fonts-noto-cjk`)だけだった
- 「ランナーがクリーンなのでシステム依存のキャッシュは無理」は思い込みで、`.deb` ファイル単位でキャッシュして `dpkg` で直接インストールする方法が有効
- APT パッケージキャッシュで CI 実行時間を 50〜60% 削減できた(36〜45 秒 → 16〜19 秒)
- 既存の `cache-apt-pkgs-action` は速いが Node.js 20 廃止リスクがあり、現時点では自前実装がリスクと速度のバランスが良い

## 参考

https://github.com/vercel-labs/agent-browser

https://github.com/awalsh128/cache-apt-pkgs-action

https://github.com/awalsh128/cache-apt-pkgs-action/issues/193

https://github.com/gerlero/apt-install

https://github.com/Suntory-N-Water/github-actions-agent-browser-cache-strategy

[^apt]: Advanced Package Tool の略。Ubuntu などの GNU/Linux ディストリビューションで使われるパッケージ管理システムです。
[^dpkg]: `.deb` パッケージファイルを直接インストール・管理するローレベルのツール。`apt-get` の内部でも使われています。
[^composite]: GitHub Actions の composite action は、複数のステップをまとめた再利用可能なアクションを YAML と shell script だけで定義する方式。Node.js 等のランタイムが不要な点が特徴です。
    