Skipbox

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

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 円の定額で、実行時間は無制限です。

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

  • ジョブごとに新しい VM。 GitHub のランナーのリファレンスのとおり、GitHub ホストランナーはどれも GitHub が用意する新しい仮想マシンです。前のジョブがビルドしたものも pull したものも、そこにはありません。
  • 空のビルダー。 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 のキャッシュのバックエンドと GitHub Actions でのキャッシュから:

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

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

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

- 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 を挙げています。実行のたびに、ダウンロードとアップロードがもう 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 は外せます。
  • ディスク: ジョブのあとでディスクがほぼいっぱいになるまで何も消しません。いっぱいになったら、まずタグのないイメージを、次に使われたのがいちばん古いビルドキャッシュから消します。
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

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

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

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

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

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

セキュリティ

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

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

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

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

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

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

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