# GitHub Actions で Docker のレイヤーキャッシュが毎回消える理由と、ビルドを速くする方法

> GitHub Actions のジョブは毎回新しい VM で始まり、Docker のビルドキャッシュも pull したイメージもありません。setup-buildx-action の既定のビルダーも空から始まります。buildx の cache-to / cache-from（gha・registry・local）はレイヤーをダウンロードで戻すだけで、GitHub の 10 GB の枠の中です。Docker のビルドキャッシュがディスクに残るランナーなら、どれも要りません。Skipbox は月 6,000 円の定額です。

GitHub Actions で Docker のレイヤーキャッシュが毎回消えるのは、GitHub ホストランナーのジョブが毎回新しい VM で始まるからです。ビルドキャッシュも、pull したイメージもありません。docker/setup-buildx-action の既定のビルダー（`docker-container`）も、ジョブのたびに空で作られ、終わると消されます。よくある対策は、buildx でキャッシュを書き出すことです。`cache-to: type=gha,mode=max` と `cache-from: type=gha` はレイヤーを GitHub の Actions のキャッシュに置きます。枠はリポジトリごと 10 GB で、ほかのキャッシュと共用です。`type=registry` ならレジストリに、`type=local` なら actions/cache で保存するディレクトリに置きます。どれも、ビルドし直すかわりに、毎回アップロードとダウンロードをすることになります。

Skipbox なら、レイヤーはビルドするマシンに残ります。ジョブはアカウント専用のマシンで動き、Docker のイメージ、レイヤー、ビルドキャッシュをジョブのあとも残します。ディスクは S で 80 GB、GitHub のランナーは 14 GB です。`docker build` と `docker compose build` は前のビルドのレイヤーを使い、compose が pull したイメージもディスクにあります。setup-buildx-action を使うなら `driver: docker` を足すだけ。ワークフローは `runs-on: skipbox` の 1行を書き換え、料金は月 6,000 円の定額で、実行時間は無制限です。

- GitHub はジョブごとに新しい VM。レイヤーもイメージもなく、buildx のビルダーも毎回空から
- Skipbox は Docker のビルドキャッシュとイメージが専用のマシンのディスクに残り、変わっていないレイヤーはビルドもダウンロードもしない
- 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-docker-layer-cache-github-actions-ja)

## ビルドが毎回ゼロから始まる理由

- **ジョブごとに新しい VM。** GitHub の[ランナーのリファレンス](https://docs.github.com/ja/actions/reference/runners/github-hosted-runners)のとおり、GitHub ホストランナーはどれも GitHub が用意する新しい仮想マシンです。前のジョブがビルドしたものも pull したものも、そこにはありません。
- **空のビルダー。** [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) は、既定で `docker-container` のドライバーのビルダーを作って起動し、既定でオンの `cleanup` がジョブの終わりにそのビルダーを消します。
- **ディスクは 14 GB。** プライベートリポジトリの `ubuntu-latest` は 2 CPU・メモリ 8 GB・SSD 14 GB です。大きなイメージとそのビルドキャッシュ、展開するキャッシュのアーカイブで、容量が足りなくなることがあります（no space left on device）。

## buildx のキャッシュの置き場所と、それぞれのコスト

Docker の[キャッシュのバックエンド](https://docs.docker.com/build/cache/backends/)と [GitHub Actions でのキャッシュ](https://docs.docker.com/build/ci/github-actions/cache/)から:

| バックエンド | レイヤーの置き場所 | 実行のたびにかかるもの | 制限 |
|---|---|---|---|
| `type=gha` | GitHub の Actions のキャッシュ | 新しいレイヤーのアップロードと、要るレイヤーのダウンロード | ほかのキャッシュと共用でリポジトリごと 10 GB、古いものから消える。既定の scope `buildkit` はビルドのたびに上書き。GitHub のキャッシュの API の回数制限 |
| `type=registry` | レジストリの、キャッシュ用の別のイメージ | レジストリからの pull と、新しいキャッシュの push | push できる権限でのログイン。レジストリの容量と転送量 |
| `type=inline` | イメージの中 | キャッシュを読むためのイメージの pull | `mode=min` だけ。途中のステージのレイヤーは残らない |
| `type=local` と actions/cache | actions/cache で保存するディレクトリ | ディレクトリまるごとのダウンロードと展開、そしてアップロード | `reset=true`（Buildx 0.35.0 以降）を付けないと実行のたびに大きくなる。10 GB の枠を使う |

どのバックエンドでも、[Dockerfile の順番](https://docs.docker.com/build/cache/optimize/)は、変わりにくいレイヤーを先に置きます。ロックファイルをコピーして依存パッケージを入れてから、ソースをコピーします。逆にすると、コミットのたびにインストールのレイヤーが無効になり、どのキャッシュでも戻りません。

既定の `mode=min` が残すのは、最終的なイメージのレイヤーだけです。`mode=max` はすべてのステージのレイヤーを残すので、マルチステージのビルドがよく当たるかわりに、アップロードも増えます。

`type=gha` のよくある書き方です。イメージごとに scope を分けます。

```yaml
- uses: docker/setup-buildx-action@v4
- uses: docker/build-push-action@v7
  with:
    context: .
    tags: app:ci
    load: true
    cache-from: type=gha,scope=app
    cache-to: type=gha,scope=app,mode=max
```

**キャッシュマウントは残りません。** `RUN --mount=type=cache`（ビルドの中のパッケージマネージャーのキャッシュ）は、既定では GitHub Actions のキャッシュに入りません。Docker のドキュメントは、actions/cache との出し入れに [buildkit-cache-dance](https://github.com/reproducible-containers/buildkit-cache-dance) を挙げています。実行のたびに、ダウンロードとアップロードがもう 1 組増えます。

## docker compose は毎回すべてのイメージを pull する

`docker compose up` で Postgres や Redis やいくつかのサービスを立ち上げる結合テストは、VM が毎回新しいので、ジョブのたびにすべてのイメージを pull します。GitHub ホストランナーでできるのは、ダウンロードの場所を移すことだけです。

- `docker save` でイメージを tar にし、actions/cache で保存して、次のジョブで `docker load` する。10 GB の枠を使い、読み込みにも時間がかかります。
- サービスのイメージを buildx と上のキャッシュでビルドする。ベースのイメージの pull は残ります。

## Docker のキャッシュが残るマシンが答えになるとき

上のどのバックエンドも、ビルダーが忘れるから要るものです。ジョブのあとも残る Docker なら、前のビルドのレイヤーも、キャッシュマウントも、pull したイメージも、はじめから自分のディスクにあります。書き出すものも読み込むものもなく、10 GB の枠を分け合うこともありません。

## Skipbox なら Docker のキャッシュがマシンに残る

プランごとに、アカウント専用のマシンが付き、ジョブのあとも残ります。

- **`docker build` と `docker compose build`** は、ワークフローを変えずに、前のビルドのレイヤーを使います。
- **イメージはディスクに残ります。** `docker compose up` は前に pull したイメージを見つけ、`docker compose pull` は変わったレイヤーだけを取ってきます。
- **キャッシュマウントも** Docker のビルドキャッシュと一緒に残るので、`RUN --mount=type=cache` のパッケージのキャッシュは、手順を足さずに次のビルドで使えます。
- **docker/setup-buildx-action には `driver: docker` を足します。** 新しい空のビルダーではなくマシンの Docker でビルドするようになり、`cache-from` と `cache-to` は外せます。
- **ディスク:** ジョブのあとでディスクがほぼいっぱいになるまで何も消しません。いっぱいになったら、まずタグのないイメージを、次に使われたのがいちばん古いビルドキャッシュから消します。

```yaml
jobs:
  build:
    runs-on: skipbox # ubuntu-latest から書き換える
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v4
        with:
          driver: docker # マシンの Docker とそのビルドキャッシュを使う
      - uses: docker/build-push-action@v7
        with:
          context: .
          tags: app:ci
          load: true
```

マシンの大きさは、ジョブのラベルで選びます。

| ラベル | マシン | ディスク | プラン |
|---|---|---|---|
| `skipbox` | 4 vCPU・メモリ 16 GB | 80 GB | S、月 6,000 円 |
| `skipbox-8x16` | 8 vCPU・メモリ 16 GB | 160 GB | M、月 12,000 円 |
| `skipbox-16x32` | 16 vCPU・メモリ 32 GB | 320 GB | L、月 23,000 円 |

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

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

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

最初のジョブはすべてのレイヤーをビルドします。2 回目は、それをマシンの上で見つけます。

## セキュリティ

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

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

## 自分の Docker のビルドで試す

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

## よくある質問

### GitHub Actions で Docker のレイヤーキャッシュが毎回効かないのはなぜ？

GitHub ホストランナーのジョブは毎回新しい VM で始まり、前の実行のビルドキャッシュもイメージも残っていないからです。docker/setup-buildx-action も、既定では docker-container のビルダーを空の状態で作り、ジョブの終わりに消します。だから cache-to で書き出して cache-from で読み込むか、Docker のビルドキャッシュが残るマシンで動かさない限り、レイヤーは毎回ビルドし直しです。

### GitHub Actions で Docker のレイヤーをキャッシュするには？

GitHub ホストランナーでは、buildx のキャッシュの書き出し先を使います。docker/build-push-action に cache-from: type=gha と cache-to: type=gha,mode=max を書くと、レイヤーは GitHub の Actions のキャッシュ（リポジトリごと 10 GB、ほかのキャッシュと共用）に入ります。type=registry ならレジストリの別のイメージに、type=local なら actions/cache で保存するディレクトリに入ります。type=gha では、イメージごとに scope を分けます。既定の scope はビルドのたびに上書きされるからです。ディスクが残るマシンなら、書き出し先なしで docker build がレイヤーを使い回します。

### buildx の cache-to type=gha でもビルドが遅いのはなぜ？

レイヤーが行き来しているからです。mode=max では、実行のたびにすべてのステージの新しいレイヤーをアップロードし、次の実行が要るレイヤーをダウンロードします。キャッシュはほかのものと同じリポジトリごと 10 GB の枠を使うので消されやすく、リクエストが多いと GitHub のキャッシュの API に回数を制限されます。レイヤーがはじめからディスクにあるビルダーなら、どちらの向きの転送もありません。

### docker compose のテストで、毎回同じイメージを pull しないで済ませるには？

GitHub ホストランナーでは避けられず、ダウンロードの場所を移すことしかできません。docker save でイメージを tar にし、actions/cache で保存して、次のジョブで docker load します。同じ 10 GB の枠を使い、毎回ダウンロードと読み込みが入ります。ディスクが残るマシンなら、前に pull したイメージがそのままあり、docker compose pull は変わったレイヤーだけを取ってきます。

### Skipbox で Docker のキャッシュを残すのに、ワークフローの変更は要る？

docker build と docker compose build なら、runs-on を書き換えるだけです。専用のマシンが Docker のイメージ、レイヤー、ビルドキャッシュをジョブのあとも残します。docker/setup-buildx-action を使っているなら、driver: docker を足してください。新しい空のビルダーではなくマシンの Docker でビルドするようになり、cache-from と cache-to は外せます。

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

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

2026-11-13 更新 · https://skipbox.dev/ja/guides/docker-layer-cache-github-actions
