GitHub Actions を高速化する方法:20分かかる CI を、効く順に短くする
PR ごとに 20 分かかる GitHub Actions は、たいてい 4 つの遅れが重なっています。ジョブどうしの待ち合わせ、要らない実行、まっさらな VM がジョブのたびにくり返す準備、そして 2 コアに押し込まれた重いステップです(プライベートリポジトリの ubuntu-latest は 2 CPU・メモリ 8 GB)。直す順番はこうです。まず時間を決めている列を測り、要らない実行をなくし、独立したジョブを同時に動かす。次に準備のやり直しをなくし、重いステップにコアを足し、まだ長いテストだけを分割する。前半の 3 つはワークフローの直しで、お金はかかりません。後半はマシンの問題で、キャッシュがジョブのあとも残り、コアの多いランナーが答えです。
Skipbox は、そのランナーです。ジョブはアカウント専用のマシンで動き、Docker のレイヤー、パッケージのキャッシュ、ツール、ビルドの結果がジョブのあとも残ります。4〜16 vCPU を、月 6,000 円の定額で、実行時間は無制限。astro の CI は GitHub で 528 秒、Skipbox で 30 秒でした。移行は runs-on: skipbox の 1行です。
1. 時間を決めている列を測る
チームが待っているのは、最後に終わるジョブの列です。全ジョブの合計ではありません。代表的なプルリクエストの実行を開くと、グラフでどのジョブがどれを待っているかが見え、各ジョブのログでステップの時間がわかります。いちばん遅い列について、次を書き出します。
| 段階 | 見るところ |
|---|---|
| キュー待ち | ランナーを待っていたか。コマンドを速くしても、キューは短くならない |
| ジョブの準備、チェックアウト | コンテナのイメージの取得、浅いクローンで足りるのに全部クローンしていないか |
| ツールチェーンとインストール | キャッシュが当たったか。当たったぶん、復元にかかった時間より得をしたか |
| ビルド | 同じものを別のジョブでもビルドしていないか |
| テスト | CPU を使い切っているか。ワーカーが 1 つで全部やっていないか |
| アップロード | 肝心の処理が終わったあとの、アーティファクトとキャッシュの保存 |
キャッシュが当たった実行と、外れた実行を分けて比べます。半分外れるキャッシュも、平均すると効いているように見えます。
2. 要らない実行をなくす
- 古い実行をキャンセルする。 2 回目の push のあと、動いている 1 回目の実行は無駄です。
cancel-in-progressを付けた concurrency のグループで止めます。デプロイを途中で止めないよう、プルリクエストだけに効かせます。
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
- 変更に関係するものだけを動かす。
paths:でドキュメントだけの変更を飛ばせます。モノレポならturbo run test --affectedや Nx の affected で、変わったパッケージだけを動かします。必須チェックをトリガーの段階で飛ばすと結果が返らずマージできなくなるので、必須のものはジョブの中で判定します。
3. 独立したジョブを同時に動かす
GitHub は、needs: でつながないかぎりジョブを並列に動かします。lint、型チェック、単体テスト、ビルドは、たいてい互いを待つ必要がありません。いっしょに始めて、パッケージングやデプロイだけが全部を待つようにします。同じものを 2 つのジョブでビルドしているなら、1 回だけビルドして actions/upload-artifact で渡します。アップロードとダウンロードの時間も測ってください。
4. 準備のやり直しをなくす
長い CI の時間の大半は、ここにあります。GitHub ホストランナーのジョブは毎回新しい VM で始まるので、ツールチェーンのダウンロード、依存パッケージのインストール、Docker のレイヤーのビルド、コンパイルを毎回一からやります。
- 本当に当たるキャッシュ。
actions/setup-nodeのcache: npm(pnpm・yarn も同じ)は、ロックファイルをキーにパッケージマネージャーのダウンロードを保存します。node_modulesは保存しないので、インストールは毎回走ります。GitHub のキャッシュはリポジトリごとに 10 GB まで、7日使われないと消え、当たっても新しい VM へのダウンロードです(キャッシュの上限)。 - ツールのキャッシュ。
eslint --cache、tsc --incremental、Jest のキャッシュ、turbo の.turbo/cacheは、ダウンロードではなく処理の結果そのものを残します。新しい VM では、それも復元しないかぎり空です。 - Docker のレイヤー。 新しい VM の
docker buildは、レイヤーなしで始まります。buildx のcache-toで書き出して毎回読み込むのが回り道です(Docker のレイヤーキャッシュ)。
ディスクが残るマシンなら、どれも何もしなくて済みます。キャッシュもツールのキャッシュもレイヤーもビルドの結果も、前のジョブのままそこにあります。
5. 重いステップにコアを足す
コンパイラ、バンドラ、テストランナーは、コアの数だけワーカーを動かします。2 CPU なら 2 つずつです。テストランナーが実際にいくつ使っているかも確かめます。Jest の既定はコアの数から 1 を引いた数(2 CPU なら 1)、pytest は -n auto を付けるまで 1 本、Playwright の既定はコアの半分で、CI ではワーカー 1 つが推奨です。コアを足して効くのは、ランナーに使わせたうえで、CPU を使い切るステップだけです。ダウンロードやデータベースの起動は速くなりません。
GitHub でコアを増やすなら大きいランナーです。4〜96 コアで、使えるのは GitHub Team か Enterprise Cloud の組織だけ、コアが増えるほど 1 分の単価も上がります。
6. まだ長いものだけを分割する
マトリクスでシャードに分けると、長いテストを複数のジョブで回せます。待ち時間は短くなりますが、分数は減りません。シャードごとにチェックアウト、インストール、ビルドをやり直すので、4 分割なら準備が 4 回です。ファイルの数ではなく測った時間で均し、準備がシャードの大半を占めるようになったら、それ以上は分けません。
遅いままだと、何がかかるか
15 分の CI を 1 日 40 回動かすなら、チーム全体で毎日 10 時間の待ちです。作業の切り替えのロスは、まだ数えていません。分課金では、準備の分数もテストの分数と同じように請求されます。
| 月の実行時間(4 vCPU) | GitHub | Skipbox S | 毎月の差額 |
|---|---|---|---|
| 10,000分 | 参考値:2026年11月10日の為替レート・1ドル=158.22円。米ドルの料金を、このレートで円に換算した概算です。 | 6,000 円 | 参考値:2026年11月10日の為替レート・1ドル=158.22円。米ドルの料金を、このレートで円に換算した概算です。 |
| 20,000分 | 参考値:2026年11月10日の為替レート・1ドル=158.22円。米ドルの料金を、このレートで円に換算した概算です。 | 6,000 円 | 参考値:2026年11月10日の為替レート・1ドル=158.22円。米ドルの料金を、このレートで円に換算した概算です。 |
| 30,000分 | 参考値:2026年11月10日の為替レート・1ドル=158.22円。米ドルの料金を、このレートで円に換算した概算です。 | 6,000 円 | 参考値:2026年11月10日の為替レート・1ドル=158.22円。米ドルの料金を、このレートで円に換算した概算です。 |
GitHub はコアが多いほど 1 分の単価が上がるので、マシンが大きいほど差が開きます。
| 月 20,000分 | GitHub | Skipbox | 毎月の差額 |
|---|---|---|---|
| 8 vCPU | 参考値:2026年11月10日の為替レート・1ドル=158.22円。米ドルの料金を、このレートで円に換算した概算です。 | 12,000 円(M) | 参考値:2026年11月10日の為替レート・1ドル=158.22円。米ドルの料金を、このレートで円に換算した概算です。 |
| 16 vCPU | 参考値:2026年11月10日の為替レート・1ドル=158.22円。米ドルの料金を、このレートで円に換算した概算です。 | 23,000 円(L) | 参考値:2026年11月10日の為替レート・1ドル=158.22円。米ドルの料金を、このレートで円に換算した概算です。 |
GitHub の額は、GitHub Team に含まれる無料分を引いたあとの請求です。自分のチームの分数は、トップページの料金計算で出せます。
マシンが答えになるとき
実在のプロジェクトの CI を、ステップはそのままで、GitHub の公開リポジトリのランナー(4 vCPU・16 GB)と、同じ vCPU の Skipbox の専用のマシンで動かした結果です(くわしい結果)。
| プロジェクト | GitHub | Skipbox、専用のマシン | マシンに残っていたもの |
|---|---|---|---|
| astro(Node.js、Turborepo) | 528 秒 | 30 秒 | turbo のキャッシュ。40 のビルドのタスクを結果から戻した |
| tauri(Rust) | 246 秒 | 53 秒 | Cargo の target/。クレートを 1 つもコンパイルし直さなかった |
| ripgrep(Rust) | 200 秒 | 40 秒 | cross の target/ と Docker のイメージ |
| vite(Node.js) | 141 秒 | 100 秒 | パッケージのキャッシュ。残りはテストそのものの時間 |
プライベートリポジトリの ubuntu-latest は、この GitHub 側の半分のコアです。
Skipbox に替えると
マシンが残る
プランごとに、アカウント専用のマシンが付き、ジョブのあとも残ります。Docker のイメージ・レイヤー・ビルドキャッシュ、パッケージのキャッシュ、ツール、そしてチェックアウトしたコードと Cargo の target/、turbo のキャッシュがディスクに残ります。GitHub 公式の 7倍以上のキャッシュ容量で、キーの設定も、7日の期限も、ブランチの区切りもありません。
コアを足しても定額
マシンの大きさは、ジョブのラベルで選びます。
| ラベル | マシン | プラン |
|---|---|---|
skipbox | 4 vCPU・メモリ 16 GB | S、月 6,000 円 |
skipbox-8x16 | 8 vCPU・メモリ 16 GB | M、月 12,000 円 |
skipbox-16x32 | 16 vCPU・メモリ 32 GB | L、月 23,000 円 |
実行時間は無制限なので、シャードも再実行も大きなマトリクスも、請求は変わりません。Skipbox のジョブは GitHub の分数を使わないので、GitHub の予算で止まることもありません。
マシンの管理は要らない
マシンの用意、イメージの更新、ジョブごとの後片付け、ヘルスチェックと自動復旧まで、Skipbox が行います。
移行は runs-on を 1行書き換えるだけ
jobs:
test:
runs-on: skipbox # ubuntu-latest から書き換える
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci && npm test
- Skipbox に GitHub でログインする
- Skipbox の GitHub アプリを入れて、使うリポジトリを選ぶ
runs-onを書き換えて push する
ステップやアクションはそのまま、Docker、Node.js、Python の入った Ubuntu 24.04 で動きます。
セキュリティ
- マシンはアカウントごとの専用です。ほかの会社のジョブと同じマシンで動くことはありません。
- ジョブが終わるたびに、ランナーの認証情報、プロセスとコンテナを消し、残したチェックアウトからも git の認証情報と追跡していないファイルを消します。「毎回クリーン」なら、ジョブごとに新しいマシンで動きます。
- 書き込み権限のない人のコード(フォークからのプルリクエストなど)は、使い捨ての VM で動きます。キャッシュは読めますが、書き換えられません。
くわしくはジョブの分離のしくみにあります。
いちばん遅いジョブで試す
カード不要で、7日間かジョブの合計 3,000分まで無料です。時間を決めている列のジョブを runs-on: skipbox に替えて 2 回動かし、2 回目のステップの時間を GitHub と並べてください。
よくある質問
PR ごとに CI で 20 分以上待たされる。高速化するにはどうすればいい?
効く順に直します。まず実行のグラフとステップの時間を見て、全体の時間を決めているジョブの列を探します(キュー待ちは別に数えます)。次に、古い実行のキャンセルとパスでの絞り込みで、要らない実行をなくします。習慣で書いた needs: を外して、独立したジョブを同時に動かします。そのうえで、ジョブごとの準備のやり直しをなくし、重いステップにコアを足し、まだ長いテストだけを分割します。プライベートリポジトリの ubuntu-latest(2 CPU・メモリ 8 GB)では、後ろの 3 つが 20 分の大半を占めることが多く、どれもマシンの問題です。Skipbox は、キャッシュがジョブのあとも残る専用のマシンで、4〜16 vCPU を使えます。
ランナーを替えるのとワークフローを最適化するのは、どちらが効く?
先にワークフローの無料の直しをします。古い実行のキャンセル、パスでの絞り込み、要らない needs: の削除は、どのランナーでもやってくれません。そのあとに残る時間を見ます。ジョブごとにくり返す準備(ツールチェーン、依存パッケージ、Docker のビルド)と、コアを使い切るステップは、マシンで決まります。キャッシュが残り、コアの多いランナーに替えれば、runs-on の 1行で消えます。ネットワークの待ちと、1 本道の処理は、マシンを大きくしても速くなりません。
GitHub Actions はなぜローカルより遅い?
手元の PC は、パッケージもツールも Docker のレイヤーもビルドの結果もディスクに残っていて、コアも 8 以上あることが多いからです。プライベートリポジトリの ubuntu-latest は 2 CPU・メモリ 8 GB で、毎回それが何もない新しい VM です。Skipbox は手元の PC と同じで、マシンがジョブのあとも残り、4 vCPU からです。
テストを分割(シャード)すると速くなる?
待ち時間は短くなりますが、分数は減りません。シャードはそれぞれ新しいジョブなので、チェックアウト、インストール、ビルドをやり直します。4 分割なら準備が 4 回で、分課金ではそのすべてに請求が来ます。準備が軽くなり、コアを使い切ってから分割するのが順番です。Skipbox なら準備がディスクに残り、実行時間は無制限なので、分割しても請求は増えません。
GitHub Actions のランナーを 4 コアより大きくするには?
GitHub では、大きいランナー(4〜96 コア)が使えるのは GitHub Team か Enterprise Cloud の組織だけで、分課金です。Skipbox なら runs-on を skipbox-8x16 にすると 8 vCPU、skipbox-16x32 にすると 16 vCPU で、月額は定額。組織でも個人アカウントでも使えます。
試すのにお金はかかる?
かかりません。カード不要で、7日間かジョブの合計 3,000分まで無料です。いちばん遅いジョブを runs-on: skipbox に替えて 2 回動かし、ステップの時間を比べてください。
2026年11月13日 更新