Skipbox

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

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 円の定額です。

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

rust-cache の README とソースコードから:

  • キャッシュするもの: ~/.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)。

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 秒)。コールドの実行のあとの 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 秒、ripgrep が 161 秒、helix が 442 秒でした。

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 秒でした。

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

astro は、target/ と同じことが Turborepo で起きています。専用のマシンが turbo のキャッシュ(turbo 2)を残すので、2回目以降は 40 のビルドのタスクとテストのタスクを動かさず、キャッシュから結果を戻します(31 秒、28 秒)。

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

実行時間は無制限です。4 vCPU を月 20,000分使うと、GitHub は 参考値:2026年11月10日の為替レート・1ドル=158.22円。米ドルの料金を、このレートで円に換算した概算です。、Skipbox S は 6,000 円で、毎月 参考値:2026年11月10日の為替レート・1ドル=158.22円。米ドルの料金を、このレートで円に換算した概算です。安くなります。

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

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/ は残りません。

くわしくはジョブの分離のしくみにあります。

自分の 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日 更新

常に warm なキャッシュで CI/CD を高速化するなら

専用ランナーを定額で。実行時間は無制限

フル稼働なら GitHub の 1/12 の費用。定額だから一日中動かせる。GitHub の 7倍のキャッシュ容量。

  • カード不要
  • GitHub でログイン
  • いつでも解約