できない.dev

Docker build でキャッシュが効いて変更が反映されない

Docker build はレイヤーごとにキャッシュを再利用するため、COPY 元のファイル変更を検知させないと古いレイヤーが使われる。
--no-cache で全レイヤー無効化、--build-arg にビルド日時を渡す、COPY 順を依存とソースで分ける、で対処する。

公開: 更新:

実行例あり(2026-09-28 に実環境で検証)

要約

Docker build は各命令を「レイヤー」として保存し、入力(命令文 + 参照ファイル)が一致する限り再利用する。
変更を反映したいのに古いレイヤーが使われる場合は、--no-cache で全無効化するか、--no-cache-filter <stage> で対象 stage だけやり直す。docker build --build-arg CACHEBUST=$(date +%s) で特定の ARG 以降を強制再実行する手法も常套。

実行例

最初に一度 build したイメージの /built-at にはその時の時刻と UUID が書かれ、同じ Dockerfile のまま build し直すと RUN の行は CACHED と表示されて /built-at も最初の値のままだった。--no-cache を付けた build と、--build-arg で CACHEBUST の値を変えた build では、どちらも /built-at が新しい値に書き換わっている。

$ docker run --rm cache-demo cat /built-at
2026-09-28T01:06:16Z d8169aa6-7e2c-46f8-ae07-960b7beb38a4
$ docker build --progress=plain -t cache-demo .
#0 building with "desktop-linux" instance using docker driver
 
#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 157B done
#1 DONE 0.0s
 
#2 [internal] load metadata for docker.io/library/alpine:3
#2 DONE 0.0s
 
#3 [internal] load .dockerignore
#3 transferring context: 2B done
#3 DONE 0.0s
 
#4 [1/2] FROM docker.io/library/alpine:3@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6
#4 resolve docker.io/library/alpine:3@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 0.0s done
#4 DONE 0.0s
 
#5 [2/2] RUN echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) $(cat /proc/sys/kernel/random/uuid)" > /built-at
#5 CACHED
 
#6 exporting to image
#6 exporting layers done
#6 exporting manifest sha256:d1bcb0ccaf52b9887904296cd0094bc8ea3d8233f6b7de03bd8206e47d666a54 done
#6 exporting config sha256:ccbd9e72f6e9e731901a7e6d2f37d9ebceab2c4b98a74743c6d6ae0ffef75561 0.0s done
#6 exporting attestation manifest sha256:a6f260e1813652a47a904d2cf38f63e5599c98ff5a1683615644f3ed3a722a7c
#6 exporting attestation manifest sha256:a6f260e1813652a47a904d2cf38f63e5599c98ff5a1683615644f3ed3a722a7c 0.0s done
#6 exporting manifest list sha256:6ad9ed5a6814b3ae9ef8e17d0d6e3ec3a3015afa1acec7c52dfd180566d71239 done
#6 naming to docker.io/library/cache-demo:latest done
#6 unpacking to docker.io/library/cache-demo:latest done
#6 DONE 0.1s
$ docker run --rm cache-demo cat /built-at
2026-09-28T01:06:16Z d8169aa6-7e2c-46f8-ae07-960b7beb38a4
$ docker build --no-cache -q -t cache-demo .
sha256:b9b0dbb881130ea6965db9090dbcdbe41e4f2c2993dd7538b304ce0d75ffa129
$ docker run --rm cache-demo cat /built-at
2026-09-28T01:06:20Z 54328ed4-593a-4335-8b0a-74aca5197bdf
$ docker build --build-arg CACHEBUST=$(date +%s) -q -t cache-demo .
sha256:e6ee116164c79d77f516db8d52c6b891a0c1264d3e240d1727b4859c10d893aa
$ docker run --rm cache-demo cat /built-at
2026-09-28T01:06:22Z 867c9447-8c6f-48f7-901e-1763b53c2b2a

— 2026-09-28 時点の出力

検証環境

検証日
実行環境
local host (Windows 11 Home, Docker 29.8.0)
バージョン
  • Node.js 22.14.0
  • npm 10.9.2
  • Git 2.48.1.windows.1
  • Docker 29.8.0

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

よくある原因

  1. .dockerignore で対象ファイルを除外しており、変更があってもビルドコンテキストに入らないため、Docker からは「入力が変わっていない」と見える。
  2. RUN apt-get install ... のような命令は引数が同じ限りキャッシュされる。
    外側のミラーで apt リポジトリが更新されていても、Docker はキャッシュを使い続ける。
  3. COPY . . を依存インストールより前に書いてしまうと、ソースを 1 行直すたびに後段の npm install まで毎回やり直しになる。
    逆に依存ファイルだけ更新したつもりが、依存レイヤーの後ろの工程まで再評価されない。
  4. BuildKit の --mount=type=cache,target=/root/.cache を別ステージ間で共有していて、片方の更新が反映されないように見える。

解決策

1. 全レイヤーを無効化する

docker build --no-cache -t myimg .

確実だが当然遅い。
CI や検証ビルドのみで使うのが無難。

2. 特定 stage だけ無効化する(Docker 23+)

docker build --no-cache-filter builder -t myimg .

AS builder と書いた stage だけキャッシュを捨て、他は再利用する。
詳細は 公式ドキュメント (Cache invalidation)(新しいタブで開く)。

3. 依存とソースで COPY を分ける

FROM node:20
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

package*.json を先に COPY して install を済ませてからソースをコピーすれば、ソースだけ変えたときは npm ci のレイヤーが再利用される。

4. ARG でキャッシュを意図的に壊す

ARG CACHEBUST=1
RUN curl -fsSL https://example.com/install.sh | sh
docker build --build-arg CACHEBUST=$(date +%s) -t myimg .

ARG 行の値が変わるとそれ以降の RUN がキャッシュミスになるので、外部 URL から毎回最新を取りたい場面で便利。

この記事は役立ちましたか?