できない.dev

docker compose up で Dockerfile の変更が反映されない

docker compose up は既定ではビルド済みイメージをそのまま使い、Dockerfile やソースを変更しても再ビルドしない。
--build を付けて起動するのが基本。
それでも反映されない場合はレイヤーキャッシュとボリュームの覆い被せを疑う。

公開: 更新:

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

要約

Dockerfile やソースコードを変更したのに docker compose up で反映されないのは、up が既定では再ビルドしないためです。
up はビルド済みイメージが存在すればそれをそのまま使ってコンテナを起動します。
変更を反映するには --build フラグを付けるのが基本です(docker compose up リファレンス(新しいタブで開く))。
それでも反映されない場合は、レイヤーキャッシュとボリュームによる覆い被せを順に確認します。

実行例

message.txt を v2 に書き換えてから docker compose up -d をやり直しても、イメージのビルドは走らず既存のコンテナが起動し直されただけで、ログは message: v1 のままだった。--build を付けて起動するとイメージが作り直されてコンテナが再作成され、message: v2 が出力されている。

$ docker compose up -d
Image compose-rebuild-demo-web Building 
#1 [internal] load local bake definitions
#1 reading from stdin 593B 0.0s done
#1 DONE 0.0s
 
#2 [internal] load build definition from Dockerfile
#2 transferring dockerfile: 109B done
#2 DONE 0.0s
 
#3 [internal] load metadata for docker.io/library/alpine:3
#3 DONE 0.0s
 
#4 [internal] load .dockerignore
#4 transferring context: 2B done
#4 DONE 0.0s
 
#5 [1/2] FROM docker.io/library/alpine:3@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
#5 resolve docker.io/library/alpine:3@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b 0.0s done
#5 DONE 0.0s
 
#6 [internal] load build context
#6 transferring context: 50B done
#6 DONE 0.0s
 
#7 [2/2] COPY message.txt /message.txt
#7 CACHED
 
#8 exporting to image
#8 exporting layers done
#8 exporting manifest sha256:66b19322543c5bb8f95c2216120450b949fc292968e8413c0f8b61e65744e125 done
#8 exporting config sha256:7ea035c1eb721e281d89e975c2d0afe70f4e510a1ae0ffdd87c1653a01a6194a done
#8 exporting attestation manifest sha256:def497c9b663fba09bc64c67241d71d0fe9fda53ffd6c8c1386c5a8d2d9d58c5 0.0s done
#8 exporting manifest list sha256:9a2625de0f5d38e99e4d9febc17d1e796e186ffccb9027bc2d75711bbbfc2d38 done
#8 naming to docker.io/library/compose-rebuild-demo-web:latest done
#8 unpacking to docker.io/library/compose-rebuild-demo-web:latest 0.0s done
#8 DONE 0.1s
 
#9 resolving provenance for metadata file
#9 DONE 0.0s
 Image compose-rebuild-demo-web Built 
 Network compose-rebuild-demo_default Creating 
 Network compose-rebuild-demo_default Created 
 Container compose-rebuild-demo-web-1 Creating 
 Container compose-rebuild-demo-web-1 Created 
 Container compose-rebuild-demo-web-1 Starting 
 Container compose-rebuild-demo-web-1 Started
$ docker compose logs --tail 1 web
web-1  | message: v1
$ cat message.txt
message: v2
$ docker compose up -d
Container compose-rebuild-demo-web-1 Starting 
 Container compose-rebuild-demo-web-1 Started
$ docker compose logs --tail 1 web
web-1  | message: v1
$ docker compose up --build -d
Image compose-rebuild-demo-web Building 
#1 [internal] load local bake definitions
#1 reading from stdin 593B 0.0s done
#1 DONE 0.0s
 
#2 [internal] load build definition from Dockerfile
#2 transferring dockerfile: 109B done
#2 DONE 0.0s
 
#3 [internal] load metadata for docker.io/library/alpine:3
#3 DONE 0.0s
 
#4 [internal] load .dockerignore
#4 transferring context: 2B done
#4 DONE 0.0s
 
#5 [internal] load build context
#5 transferring context: 50B done
#5 DONE 0.0s
 
#6 [1/2] FROM docker.io/library/alpine:3@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
#6 resolve docker.io/library/alpine:3@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b 0.0s done
#6 DONE 0.0s
 
#7 [2/2] COPY message.txt /message.txt
#7 CACHED
 
#8 exporting to image
#8 exporting layers done
#8 exporting manifest sha256:39f59196c1cfe72ff268499ab5c0af333c37d6844f2d1f34ef21bdc04414fbba done
#8 exporting config sha256:24d4cbbd5b922fd965a470a93053d02ef47a1f6846aa45772910d25efd0a57eb done
#8 exporting attestation manifest sha256:bb21d64785e1fa2c08daf7c4bf79a1e642b1703cc4561e139ee5ed844c00c333 0.0s done
#8 exporting manifest list sha256:bad2b7ace69453a35e2627c30acd4bca7c46939516955c44c14892a5cbab01a6 done
#8 naming to docker.io/library/compose-rebuild-demo-web:latest done
#8 unpacking to docker.io/library/compose-rebuild-demo-web:latest 0.0s done
#8 DONE 0.1s
 
#9 resolving provenance for metadata file
#9 DONE 0.0s
 Image compose-rebuild-demo-web Built 
 Container compose-rebuild-demo-web-1 Recreate 
 Container compose-rebuild-demo-web-1 Recreated 
 Container compose-rebuild-demo-web-1 Starting 
 Container compose-rebuild-demo-web-1 Started
$ docker compose logs --tail 1 web
web-1  | message: v2

— 2026-09-14 時点の出力

検証環境

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

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

よくある原因

  1. up は再ビルドしない: build: を定義していても、イメージが既にあれば up はビルドを省略する。
    Dockerfile を編集しただけでは次の up に反映されない。
  2. レイヤーキャッシュ: --build を付けても、COPY 対象に変化が無いと判断されたレイヤー以降のみ再実行される。apt-get install のような外部状態に依存する手順は古いキャッシュが残りやすい。
  3. ボリュームの覆い被せ: volumes: - .:/app のような bind mount があると、イメージ内の /app はホスト側の内容で上書きされる。
    イメージをいくら作り直してもコンテナ内に見えるのはホストのファイル。
  4. 古いローカルイメージ: image: myapp:latest のような指定では、レジストリを更新してもローカルの latest タグが残っていればそちらが使われる。

解決策

1. --build を付けて起動する

docker compose up --build -d

開発中はこの形をデフォルトにしておくと「反映されない」事故を避けられます。

2. キャッシュを無効化して作り直す

docker compose build --no-cache web
docker compose up -d web

ベースイメージの更新やパッケージインストールの結果を確実に取り込みたいときは --no-cache でビルドし直します。

3. ボリューム定義を確認する

docker compose config

展開後の設定で volumes: を確認し、イメージの変更を反映したいパスが bind mount や named volume で覆われていないか確かめます。
覆われている場合、反映すべきはイメージではなくマウント元です。

4. リモートイメージを取得し直す

docker compose pull
docker compose up -d

image: 指定のサービスは pull で最新を取得してから起動します。

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