チーム開発でよく使われるDocker Composeですが、「とりあえずdocker-compose upすれば動く」フェーズを超えて、実際にローカル開発環境として使い込んでいくと、地味なところで何度もつまずきます。
この記事は、自分がDocker Composeでローカル開発環境を組む中で実際にハマった(あるいは調べていて「これは知らないとハマるな」と感じた)ポイントを、症状・原因・対処法のセットで整理したものです。
対象読者
-
docker compose upで環境を立ち上げること自体はできるが、そこから先で細かいトラブルにたびたび遭遇している方 - チームの開発環境をDocker Compose化しようとしていて、事前にありがちな落とし穴を知っておきたい方
- 「なんとなく動いているけど、なぜ動いているか分かっていない」設定(
depends_on、volumesまわりなど)がある方
逆に、Docker自体の基礎(イメージ・コンテナ・レイヤーとは何か)から知りたい方や、本番環境でのオーケストレーション(Kubernetesなど)を扱いたい方には向いていません。
前提:Compose V1とV2
現在「Docker Compose」と呼ばれているものは、docker-compose(ハイフンあり・Python製・V1)ではなく、docker compose(スペース区切り・Go製・V2、Docker CLIのサブコマンドとして組み込み)です。V1は2023年6月に完全にサポートが終了しており[^v1-eol]、これから新規に環境を組むならdocker composeコマンドを使うのが前提になります。本記事のサンプルもすべてdocker compose(V2)を前提にしています。
なお本記事執筆時点(2026年9月)でのDocker Compose最新版はv5系です[^compose-latest]。とはいえ、以下で紹介する落とし穴の多くはCompose Specの基本的な仕様に起因するもので、バージョンが上がっても考え方自体は変わりません。
ハマりどころ集
1. versionフィールドの警告が出る
症状:docker compose upのたびに、次のような警告が出る。
WARN[0000] the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
原因:docker-compose.ymlの先頭に書くversion: "3.8"のような指定は、Compose 1.27.0(2020年9月リリース)の時点ですでに不要になっています[^version-obsolete]。長らく警告なしで許容されていましたが、近年のCompose CLIでは明示的に警告が出るようになりました。
対処:素直にversion:の行を削除するだけです。Compose Specは現在、ファイル内の記法だけで自動的にパースされるため、バージョン指定自体が不要です。
- version: "3.8"
services:
web:
build: .
2. コンテナ内で作られたファイルの所有者がrootで、ホスト側から編集・削除できない
症状:コンテナ内でnpmやbundlerがインストールしたファイル、あるいはコンテナ内のプロセスが生成したログファイルなどが、ホスト側からはroot所有になっていて、Permission deniedで編集や削除ができない。
原因:多くのベースイメージ(node公式イメージなど)は、デフォルトではrootユーザーとしてコンテナ内のプロセスを実行します。bind mountはホストとコンテナでファイルシステムを共有しますが、Linuxカーネルはユーザー名ではなく数値のUID/GIDだけを見て所有権を判定するため、コンテナ内のroot(UID 0)が作ったファイルは、ホスト側から見てもroot所有として扱われます[^uid-gid]。
対処:ホスト側の実行ユーザーとコンテナ内のUID/GIDを合わせるのが基本方針です。
# compose.yaml
services:
app:
build: .
user: "${UID:-1000}:${GID:-1000}"
volumes:
- .:/app
# .envに書いておくか、実行時にexportしておく
export UID=$(id -u)
export GID=$(id -g)
docker compose up
より恒久的にはDockerfile側で非rootユーザーを作成し、そのユーザーでプロセスを実行する方法もあります。
ARG USER_UID=1000
ARG USER_GID=1000
RUN groupadd -g $USER_GID appuser && useradd -m -u $USER_UID -g $USER_GID appuser
USER appuser
3. npm installしたのにコンテナ内で"Cannot find module"になる
症状:Dockerfile内でnpm installを実行しているのに、コンテナを起動すると依存パッケージが見つからないというエラーが出る。ローカルでは動くのに、コンテナだけ動かない。
原因:volumes: - .:/appのようにホストのプロジェクトディレクトリ全体をbind mountすると、ビルド時にDockerfile側で作った/app/node_modulesが、起動時にホスト側の(node_modulesが存在しない、あるいはOSが異なる)ディレクトリで上書きされてしまいます。bind mountは「コンテナ側のディレクトリを、ホスト側の内容で置き換える」動作をするため、ビルド時の成果物が消えてしまうのが原因です。
対処:node_modulesだけをbind mountの対象から外す「anonymous volume」というテクニックを使います。
services:
app:
build: .
volumes:
- .:/app
- /app/node_modules # ここだけコンテナ内のものを優先させる
こう書くと、.:/appより後に評価される/app/node_modulesが優先され、ホスト側のnode_modules(存在しない場合も含む)で上書きされるのを防げます。
4. DBが起動しきる前にアプリが接続しにいってエラーになる
症状:docker compose up直後、アプリコンテナが「データベースに接続できません」というエラーで落ちる。少し待ってからdocker compose upし直すと動く。
原因:depends_onはデフォルトでは「依存先のコンテナが起動(start)したかどうか」しか見ておらず、「依存先のプロセスが実際にリクエストを受け付けられる状態(ready)かどうか」までは待ってくれません。PostgreSQLやMySQLはコンテナ自体はすぐ起動しますが、内部の初期化処理が終わるまでには数秒かかることがあり、その間に接続しにいくとエラーになります。
対処:healthcheckを定義した上で、depends_onにcondition: service_healthyを指定します[^healthcheck]。
services:
app:
build: .
depends_on:
db:
condition: service_healthy
db:
image: postgres:18
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
これで、dbサービスのhealthcheckが成功するまでappサービスの起動が待機されるようになります。
5. コードを変更してもホットリロードが効かない
症状:フロントエンドの開発サーバーをコンテナ内で動かしているが、ホスト側でファイルを保存してもブラウザが自動リロードされない。手動でコンテナを再起動すると変更は反映されている。
原因:多くの開発サーバー(webpack-dev-server、ViteのHMRなど)はOSのファイル変更通知(Linuxのinotifyなど)を使ってファイル変更を検知しています。しかし、bind mount経由でホスト側のファイルシステムを見ている場合、特にDocker DesktopのMac/Windows環境では、この変更通知がコンテナ内までうまく伝播しないことがあります。
対処:大きく2つの方法があります。
-
ツール側の設定でポーリング方式に切り替える(CPU負荷は上がりますが確実です)
environment: - CHOKIDAR_USEPOLLING=true # webpack系 - WATCHPACK_POLLING=true -
Compose 2.22.0(2023年10月)でGAになった
docker compose watch機能を使う[^compose-watch]。ホスト側の変更を検知して、ファイル同期やコンテナ再起動を自動化してくれる、bind mountに代わる選択肢です。services: app: build: . develop: watch: - action: sync path: ./src target: /app/src - action: rebuild path: package.jsondocker compose watch
6. .envに書いた環境変数が思った通りに反映されない
症状:.envファイルにDB_PASSWORD=xxxxと書いたのに、コンテナ内の環境変数が空だったり、意図と違う値になっていたりする。
原因:Docker Composeにおける環境変数まわりには、目的が異なる複数の仕組みがあり、これらの優先順位を混同しがちです。
- カレントディレクトリの
.envファイル:compose.yamlファイル内で${変数名}のように変数展開するための値(コンテナに直接渡されるわけではない) -
environment::コンテナに渡す環境変数を直接記述する -
env_file::外部ファイルから環境変数をまとめて読み込む
environment:とenv_file:の両方で同じキーが指定された場合は、environment:の値が優先されます。「.envに書いたから安心」と思っていたら、実はcompose.yamlの変数展開にしか使われておらず、コンテナに渡す側はenvironment:で別の値が指定されていた、というのがよくあるハマりパターンです。
対処:どの仕組みで環境変数を渡しているのか、docker compose configで最終的にレンダリングされた設定を確認する癖をつけると事故を防げます。
docker compose config
7. docker compose down -vでDBのデータを消してしまう
症状:環境をまっさらにしようとしてdocker compose down -vを実行したら、開発用DBに入れていたテストデータが全部消えてしまった。
原因:-v(--volumes)オプションは、Composeファイルで定義されたnamed volumeまで削除します。DBのデータを永続化するために使っていたvolumeも例外ではなく、一緒に消え去ります。
対処:単にコンテナを止めて作り直したいだけなら-vを付けずにdocker compose down、あるいはdocker compose down && docker compose up -dで十分なことがほとんどです。「今回は本当にデータも含めて初期化したい」という場合にだけ、意図を持って-vを付けるようにします。バックアップが必要なら、削除前にdocker compose exec db pg_dump ...のようにダンプを取っておくと安全です。
8. Apple Silicon Mac で "exec format error" になる
症状:チームメンバーの一部(Apple Silicon搭載のMac)だけ、docker compose up時にexec /usr/local/bin/xxx: exec format errorのようなエラーが出てコンテナが起動しない。Intel MacやLinuxの開発者は問題なく動いている。
原因:使っているベースイメージがlinux/amd64用にしかビルドされていない場合、Apple SiliconのCPUアーキテクチャ(arm64)と一致せず実行できないことがあります。多くの公式イメージはマルチアーキテクチャ対応が進んでいますが、社内で独自にビルドしたイメージや、一部のニッチなイメージではamd64専用のままのことがあります。
対処:Composeファイル側でプラットフォームを明示するか、イメージ自体をマルチアーキテクチャ対応でビルドし直します。
services:
legacy-tool:
image: some/legacy-image:1.0
platform: linux/amd64 # Rosetta経由のエミュレーションで動かす(実行速度は落ちる)
エミュレーションはあくまで暫定対応なので、可能であればdocker buildx build --platform linux/amd64,linux/arm64のようにマルチアーキテクチャイメージを作り直すほうが望ましいです。
まとめ
今回挙げたハマりどころの多くは、根本をたどると「bind mountはホストとコンテナのファイルシステムをそのまま共有する」「depends_onはプロセスの起動しか見ていない」「環境変数の仕組みが複数ある」といった、Compose自体の設計上の性質に起因しています。エラーメッセージだけを追って場当たり的に直すより、こうした仕組み側の前提を一度押さえておくと、初見のトラブルにも当たりを付けやすくなると感じました。
参考資料
- Define services in Docker Compose | Docker Docs
- Docker Compose: What's New, What's Changing, What's Next | Docker
- Announcing Docker Compose Watch GA Release | Docker
- Compose ファイルのバージョンとアップグレード | Docker ドキュメント
- How to Manage Permissions for Docker Shared Volumes — Dash0
- Docker 開発での node_modules マウント、Anonymous Volume という選択肢 — Zenn
- Compose における環境変数 | Docker ドキュメント
- Releases · docker/compose (GitHub)