# rust-cache を入れても Rust の CI が速くならない理由（tauri は 246 秒→53 秒）

> Swatinem/rust-cache がキャッシュするのは依存クレートだけで、ワークスペースのクレートは対象外。インクリメンタルコンパイルも切ります。だから GitHub のまっさらな VM では、キャッシュが当たっても自分のクレートを毎回コンパイルし直します。直すのは、残り続けるマシンの target/。tauri の CI ジョブは Skipbox で 246 秒から 53 秒になりました。月 6,000 円の定額です。

Rust の CI でいちばん使われているキャッシュ、Swatinem/rust-cache が残すのは依存クレートだけです。ワークスペースのクレートは、設計としてキャッシュしません。さらに `CARGO_INCREMENTAL=0` を設定します。GitHub のランナーはジョブのたびに新しい VM で始まるので、キャッシュが完全に当たったウォームのジョブでも、自分のクレートは毎回コンパイルし直しです。これは rust-cache の設定では消せません。消せるのは、前のジョブの `target/` がそのまま残っているマシンです。Cargo は、変わっていないクレートを飛ばします。

Skipbox は、その `target/` を残します。ジョブはアカウント専用のマシンで動き、チェックアウトと Cargo の `target/` をジョブのあとも残します。tauri の CI ジョブは GitHub で 246 秒、Skipbox では 53 秒。最初のウォームの実行から、コンパイルするクレートは 0 です。ワークフローで変えたのは `runs-on: skipbox` の 1行だけで、料金は月 6,000 円の定額です。

- tauri の CI ジョブは GitHub で 246 秒、Skipbox で 53 秒（4.6 倍）。実行はすべて公開しています
- `target/` が残ると、tauri のクレートは 1 つもコンパイルされず、Cargo のビルド時間は 62 秒から 2 秒足らずに
- 4 vCPU を月 20,000分使うと、GitHub は 約 35,120 円、Skipbox は 6,000 円

**GitHub Actions を月額固定・使い放題に** Skipbox は、アカウント専用のマシンで GitHub Actions のジョブを動かします。何分動かしても月 6,000 円。ジョブが終わっても Docker のレイヤーと依存パッケージのキャッシュが残るので、次のジョブはすぐ始まります。

4 vCPU のランナーを月 20,000分 使うと、GitHub 約 35,120 円 → Skipbox 6,000 円。毎月 約 29,120 円お得。

移行は `runs-on: skipbox` に 1行書き換えるだけです。

[7日間無料で試す](https://app.skipbox.dev/sign-in?ref=g-rust-cache-slow-rust-ci-ja)

## rust-cache が残すもの、残さないもの

[rust-cache の README](https://github.com/Swatinem/rust-cache#cache-details) とソースコードから:

- **キャッシュするもの:** `~/.cargo`（レジストリ、git の依存、インストールしたバイナリ）と、依存クレートのビルド結果が入った `./target`。
- **キャッシュしないもの:** 「ワークスペースのクレートそのもの」（the workspace crates themselves are not cached）。保存の前の掃除で、`target/` から依存クレート以外をすべて消します。自分のクレートのビルド結果は、`target/` そのものから消えます。
- **インクリメンタルコンパイル:** 復元のステップが、ジョブの残りに `CARGO_INCREMENTAL=0` を設定します。
- **保存するとき:** ジョブのキーのキャッシュがまだないときだけ。キーはツールチェーン、環境変数、`Cargo.lock` と `Cargo.toml` から作られ、ソースファイルは含みません。

まっさらな VM では、結果は 1 つです。ウォームのジョブは依存クレートを復元し、そのあと自分のクレートと、それに依存するクレートを、毎回まるごとコンパイルします。

## tauri のログで見る

tauri の `test` ジョブは、Ubuntu で `cargo test` のステップを 5 つ実行し、rust-cache を使っています。ウォームの実行はどれもキャッシュが完全に当たっています（ログに `full match: true`）。

| ウォームの実行 | `Compiling` の行 | Cargo のビルド時間 | ジョブ |
|---|---|---|---|
| [GitHub](https://github.com/skipbox-dev/tauri/actions/runs/43234522982) | 32 | 118 秒 | 271 秒 |
| [Skipbox、使い捨ての VM](https://github.com/skipbox-dev/tauri/actions/runs/43291768486) | 32 | 62 秒 | 146 秒 |
| [Skipbox、専用のマシン、ウォーム 1 回目](https://github.com/skipbox-dev/tauri/actions/runs/43340142076) | **0** | **約 1.5 秒** | **53 秒** |
| [Skipbox、専用のマシン、ウォーム 2 回目](https://github.com/skipbox-dev/tauri/actions/runs/43340289959) | **0** | **約 1.9 秒** | **53 秒** |

32 行は、tauri 自身のクレートと、それに依存する 2 つのプラグインです。依存クレートは 1 行も出ません。キャッシュから戻っているからです。Cargo のビルド時間は、5 つのステップの `Finished … in` の時間を足したものです。

使い捨ての VM は、CPU も戻ったキャッシュも専用のマシンと同じで、GitHub と同じ 32 個のクレートをコンパイルし直しました。`target/` には、キャッシュから戻したものしかないからです。専用のマシンでは、ウォーム 1 回目から `Compiling` の行は 0 です。Cargo は変わったものがないことを確かめるだけで、`Finished` は 1 回 0.2〜0.7 秒でした。

以前は、専用のマシンでもウォーム 1 回目はクレートを 32 個コンパイルし直していました（[ジョブ 114 秒](https://github.com/skipbox-dev/tauri/actions/runs/43307552081)）。コールドの実行のあとの rust-cache の保存が、クレートを `target/` から消していたからです。2026年11月13日から、専用のマシンは rust-cache の復元と保存がディスクから消すものを、ステップのあとで元に戻します。保存するキャッシュは同じで、クレートは `target/` に残ります。

## ジョブの時間: Rust の 3 プロジェクトと、ほかの 7 つ

実在のプロジェクトの CI ジョブを、固定したコミットで、ステップは 1 バイトも変えずに動かしました。変えたのはトリガーと `runs-on` だけです。それぞれの環境で、コールドを 1 回、ウォームを 2 回。

- **GitHub:** `ubuntu-24.04`。パブリックリポジトリの標準ランナーで、4 vCPU・16 GB。
- **Skipbox、使い捨ての VM:** 本番のランナーイメージから、ジョブごとに新しい VM。4 vCPU・8 GB。キャッシュは Skipbox のキャッシュから戻ります。
- **Skipbox、専用のマシン:** 同じ大きさ、同じホストの CPU で、ジョブのあとも残すマシン。2026年11月13日から専用のマシンが行っている、チェックアウトと `target/` を残す後片付けと、rust-cache が消すものを元に戻す処理をしています。

Skipbox 側は、Skipbox のホストの上で、利用者のマシンとは分けたラボのプロジェクトで動かしました。時間は GitHub の API の、ジョブの開始から終了まで。どの数字も、ウォーム 2 回の中央値です。専用のマシンのコールドは、tauri が [285 秒](https://github.com/skipbox-dev/tauri/actions/runs/43339483384)、ripgrep が [161 秒](https://github.com/skipbox-dev/ripgrep/actions/runs/43340538466)、helix が [442 秒](https://github.com/skipbox-dev/helix/actions/runs/43336739720)でした。

| プロジェクト | GitHub | Skipbox、使い捨ての VM | Skipbox、専用のマシン | GitHub との比 |
|---|---|---|---|---|
| tauri | 246 秒（[220](https://github.com/skipbox-dev/tauri/actions/runs/43233861304)、[271](https://github.com/skipbox-dev/tauri/actions/runs/43234522982)） | 210 秒（[273](https://github.com/skipbox-dev/tauri/actions/runs/43290912312)、[146](https://github.com/skipbox-dev/tauri/actions/runs/43291768486)） | **53 秒**（[53](https://github.com/skipbox-dev/tauri/actions/runs/43340142076)、[53](https://github.com/skipbox-dev/tauri/actions/runs/43340289959)） | **4.6 倍** |
| ripgrep | 200 秒（[194](https://github.com/skipbox-dev/ripgrep/actions/runs/43220584380)、[206](https://github.com/skipbox-dev/ripgrep/actions/runs/43221108547)） | 156 秒（[155](https://github.com/skipbox-dev/ripgrep/actions/runs/43292759363)、[156](https://github.com/skipbox-dev/ripgrep/actions/runs/43293273127)） | **40 秒**（[39](https://github.com/skipbox-dev/ripgrep/actions/runs/43340925562)、[40](https://github.com/skipbox-dev/ripgrep/actions/runs/43341036341)） | **5.0 倍** |
| helix | 330 秒（[293](https://github.com/skipbox-dev/helix/actions/runs/43227916673)、[366](https://github.com/skipbox-dev/helix/actions/runs/43228816343)） | 350 秒（[439](https://github.com/skipbox-dev/helix/actions/runs/43287815770)、[260](https://github.com/skipbox-dev/helix/actions/runs/43289134146)） | **224 秒**（[222](https://github.com/skipbox-dev/helix/actions/runs/43337749813)、[226](https://github.com/skipbox-dev/helix/actions/runs/43338273330)） | **1.5 倍** |

ripgrep は cross の Docker イメージの中でビルドします。cross は `target/` を自分で作り、「created by cross」の印を付けます。以前の専用のマシンはこの `target/` を Cargo のものと見ずにジョブのたびに消していたので、Cargo はクレートを毎回すべてコンパイルし直していました（ジョブ 108 秒）。いまは cross の `target/` も残します。ウォームの実行の `Compiling` の行は 0 で、`Finished` は 1 回 0.04〜0.05 秒です。cross のイメージも、GitHub は実行のたびにダウンロードし（ウォーム 2 回目で 30 秒）、専用のマシンはディスクに残しています。

helix はまだ 1.5 倍です。ジョブの時間の大半が、ビルドではなくテストだからです。統合テストのスイートは、テストを動かすだけで 112 秒かかります。ビルドで残るのは helix-term（と、それに依存する xtask）だけです。helix-term の build スクリプトが毎回もう一度動くので、`test` のプロファイルで 16 秒、`integration` のプロファイルで 61 秒かかります。ほかのクレートはコンパイルされません。ツールチェーンとキャッシュの復元のステップは 9〜10 秒、GitHub では 41〜46 秒でした。

ほかの言語のプロジェクトです。

| プロジェクト | 言語 | GitHub | Skipbox、専用のマシン |
|---|---|---|---|
| [astro](https://github.com/skipbox-dev/astro/actions) | Node.js | 528 秒 | **30 秒** |
| [vite](https://github.com/skipbox-dev/vite/actions) | Node.js | 141 秒 | 100 秒 |
| [prometheus](https://github.com/skipbox-dev/prometheus/actions) | Go | 335 秒 | 273 秒 |
| [cli](https://github.com/skipbox-dev/cli/actions) | Go | 163 秒 | 137 秒 |
| [pytest](https://github.com/skipbox-dev/pytest/actions) | Python | 416 秒 | 365 秒 |
| [guava](https://github.com/skipbox-dev/guava/actions) | Java | 818 秒 | 651 秒 |
| [mastodon](https://github.com/skipbox-dev/mastodon/actions) | Docker のビルド | 29 秒 | 29 秒 |

astro は、`target/` と同じことが Turborepo で起きています。専用のマシンが turbo のキャッシュ（turbo 2）を残すので、2回目以降は 40 のビルドのタスクとテストのタスクを動かさず、キャッシュから結果を戻します（[31 秒](https://github.com/skipbox-dev/astro/actions/runs/43351547473)、[28 秒](https://github.com/skipbox-dev/astro/actions/runs/43351638436)）。

mastodon のイメージのビルドは、どちらでもすべてのレイヤーを `type=gha` のキャッシュから取るので、縮める余地がありませんでした。vite の専用のマシンの数字は、成功したウォーム 1 回分です。

プライベートリポジトリの `ubuntu-latest` は 2 CPU・8 GB。上の GitHub の実行の半分のコア数です。

## 自分の CI で確かめる

1. Rust のジョブの、ウォームの実行を開きます。rust-cache のステップのログに `full match: true` と出ているものです。
2. ビルドとテストのステップで、`Compiling` の行を数えます。キャッシュから戻った依存クレートは 1 行も出ません。行が出るのは、自分のワークスペースのクレートと、それに依存するクレートです。
3. それぞれのステップの `Finished … in` の時間を足します。これが自分のクレートに毎回かかっている時間で、クレートが変わっていなければ、残った `target/` がまるごと削る時間です。

## GitHub のランナーのままでできること

- **`cache-workspace-crates: true`**（rust-cache v2.8.0 以降）。自分のクレートもキャッシュに入ります。ただし、キーが同じキャッシュがあれば保存はスキップされ、キーはソースが変わっても変わりません。キャッシュの中のクレートは、最後に保存したときのままです。インクリメンタルコンパイルは切れたままで、キャッシュはリポジトリごと 10 GB の枠の中で大きくなります。
- **sccache。** コンパイルを入力ごとにキャッシュするので、変わっていないライブラリのクレートはストレージから戻ります。リンカーを呼ぶクレートはキャッシュできません。バイナリ、`dylib`、`cdylib`、proc-macro のクレートは毎回コンパイルされ、キャッシュが当たるたびにダウンロードが入ります。
- **`target/` を残すセルフホストランナー。** マシンの運用は自分で持ちます。OS の更新、`target/` を消さないジョブごとの後片付け、そして actions/checkout の `clean: false`。既定の `git clean -ffdx` は、`.gitignore` にある `target/` もまとめて消すからです。

## Skipbox なら target/ が残る

プランごとに、アカウント専用のマシンが付き、ジョブのあとも残ります。2026年11月13日からは、チェックアウト（ファイルの時刻ごと）と Cargo の `target/` も残します。

- actions/checkout の掃除はいつもどおり動き、Cargo の `target/` だけは消しません。
- rust-cache はワークフローに入れたままで使えます。復元で、キャッシュの古いビルド結果がディスクの新しいものを上書きすることはありません。
- rust-cache の保存と復元が `target/` や `~/.cargo` から消すものは、ステップのあとでマシンが元に戻します。保存するキャッシュは変わらず、コールドの実行の次のジョブから、変わっていないクレートはコンパイルされません。
- cross が作る `target/`（「created by cross」の印のもの）も残します。
- actions/checkout はジョブのたびに `.git/HEAD` を書き直します。同じコミットを指しているあいだは、ファイルの時刻を元に戻します。`.git/HEAD` を見る build スクリプト（vergen など）が、それだけで動き直すことはありません。

マシンには Docker のイメージ、パッケージのキャッシュ、入れたツールも残るので、ジョブのほかのステップも一緒に速くなります。マシンの大きさは、ジョブのラベルで選びます。

| ラベル | マシン | プラン |
|---|---|---|
| `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 円 |

実行時間は無制限です。4 vCPU を月 20,000分使うと、GitHub は 約 35,120 円、Skipbox S は 6,000 円で、毎月 約 29,120 円安くなります。

## 移行は runs-on を 1行書き換えるだけ

```yaml
jobs:
  test:
    runs-on: skipbox # ubuntu-latest から書き換える
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo test --workspace
```

1. Skipbox に GitHub でログインする
2. Skipbox の GitHub アプリを入れて、使うリポジトリを選ぶ
3. `runs-on` を書き換えて push する

## セキュリティ

- マシンはアカウントごとの専用です。ほかの会社のジョブと同じマシンで動くことはありません。
- ジョブが終わるたびに、ランナーの認証情報、プロセスとコンテナを消し、残したチェックアウトからも git の認証情報、フック、追跡していないファイルを消します。「毎回クリーン」なら、ジョブごとに新しいマシンで動きます。
- 書き込み権限のない人のコード（フォークからのプルリクエストなど）は、使い捨ての新しいマシンで動き、`target/` は残りません。

くわしくは[ジョブの分離のしくみ](https://docs.skipbox.dev/ja/isolation)にあります。

## 自分の Rust のジョブで試す

カード不要で、7日間かジョブの合計 3,000分まで無料です。Rust のジョブを `runs-on: skipbox` に替えて 2 回動かし、2 回目の `Compiling` の行を GitHub と並べてください。

## よくある質問

### Swatinem/rust-cache はワークスペースのクレートもキャッシュする？

しません（既定の設定では）。README に「ワークスペースのクレートそのものはキャッシュしない」とあり、保存の前に、ワークスペースのクレートのビルド結果を target/ から消します。キャッシュするのは ~/.cargo と依存クレートのビルド結果で、ジョブの残りのステップには CARGO_INCREMENTAL=0 を設定します。だから GitHub のまっさらな VM では、キャッシュが完全に当たっても、ワークスペースのクレートを毎回コンパイルし直します。GitHub での tauri の CI では、ウォームの実行のたびに Compiling の行が 32 行出ていました。

### cache-workspace-crates: true にすれば速くなる？

ワークスペースのクレートもキャッシュに入ります（rust-cache v2.8.0 以降）。ただしキャッシュのキーはツールチェーン、環境変数、Cargo.lock と Cargo.toml から作られ、ソースコードは含みません。キーが同じキャッシュがすでにあれば保存はスキップされるので、キャッシュの中のクレートは最後に保存したときのままで、コミットのたびにコードとずれていきます。インクリメンタルコンパイルも切れたままです。ウォームの実行のログで、自分のクレートに Compiling が出ているなら効いていません。

### sccache を使えば Rust の CI は速くなる？

入力が変わっていないライブラリのクレートには効きます。ただしリンカーを呼ぶクレート（bin、dylib、cdylib、proc-macro）はキャッシュできず、キャッシュが当たるたびにストレージからのダウンロードが入ります。ジョブのあとも残る target/ なら、ダウンロードも要りません。Cargo が変わっていないと見たクレートは、そもそもコンパイルされません。

### Skipbox で target/ を残すのに、ワークフローの変更は要る？

要りません。runs-on を skipbox に書き換えるだけで、rust-cache も actions/checkout もそのまま使えます。専用のマシンがチェックアウトと Cargo の target/ をジョブのあとも残し、checkout の掃除は target/ に触れません。rust-cache の保存が target/ からクレートを消しても、マシンがそれを元に戻し、復元が古いビルド結果を新しいものの上に書き戻すこともありません。だから最初のウォームの実行から、変わっていないクレートはコンパイルされません。

### 試すのにお金はかかる？

かかりません。カード不要で、7日間かジョブの合計 3,000分まで無料です。Rust のジョブを runs-on: skipbox に替えて 2 回動かし、2 回目の Compiling の行とビルドのステップを GitHub と比べてください。

2026-11-13 更新 · https://skipbox.dev/ja/guides/rust-cache-slow-rust-ci
