
[Docker Compose] そのポート公開、本当に必要?
Docker がローカル開発環境の基盤のメインストリームになってから大分経ち、大抵のプロジェクトでは Docker Compose が使われていると思います。
私は普段から複数のプロジェクトに横断的に参画していて色々な Compose Stack を見ますが、不必要にホストマシンにポートを公開しているケースにたまに遭遇します。おそらく、フレームワークのテンプレートや Docker Hub で公開されている設定例を使い回してそのままにしてるパターンが多いのではないでしょうか。
そのプロジェクトだけに関わっている場合は特に問題ないんですが、複数プロジェクトに関わっていて複数の Compose Stack を使っていると、ポートの衝突が発生してサービスが起動しない問題が出てきます。そこで、今回は設定の改善案を提案しますので、これを機に今一度見直しをしてみるのはいかがでしょうか。
なお、今回の話は全てデフォルトのネットワーク設定である bridge
ドライバを使っている場合に限ります。
1. そもそも公開する必要があるかどうかを見極める
以下は適当なプロジェクトの compose.yaml
の例 (細かいところは省略) です。この様につい愚直に全サービスに ports:
をベタ書きしがちですが、まずは「本当にホスト側に公開すべきか」をしっかり見極めましょう:
services:
rails:
build:
context: .
command: bundle exec rails server -b 0.0.0.0
ports:
- "3000:3000"
- "5432:5432" # <- Vite でフロントエンドの開発サーバーも起動する想定
# ...
mysql:
image: mysql
ports:
- "3306:3306"
# ...
localstack:
image: localstack/localstack
ports:
- "4566:4566"
# ...
mailpit:
image: axllent/mailpit
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI
# ...
順番に掘り下げてみます。
- rails
- Rails server の
3000
と Vite Dev Server の5432
は実際にブラウザからのアクセスで必要。
- Rails server の
- mysql
- アプリ (Rails) からは Docker ネットワークから
mysql:3306
の様に接続するのでホストへの公開は基本的には不要。 - ただしホストから GUI などで繋ぎたい場合は必要。コンテナ内の CLI (例:
docker compose exec -T mysql mysql
) で十分なら不要。
- アプリ (Rails) からは Docker ネットワークから
- localstack (AWS リソースのモック. MinIO, DynamoDB local なども同様)
- これも MySQL と同様の理由で不要。ホストから AWS CLI で直接 API 実行したい場合でも後述の方法で可能。
- mailpit (SMTP のモック. MailCatcher や MailHog も同様)
- SMTP の
1025
も、MySQL や LocalStack と同様の理由で不要。 - Web UI の
8025
はブラウザからアクセスしたいので必要。
- SMTP の
要するにホストマシンから直接 127.0.0.1
で繋ぎたいサーバーなどで原則公開する様にしましょう。以下は設定例です:
services:
rails:
build:
context: .
command: bundle exec rails server -b 0.0.0.0
ports:
- "3000:3000"
- "5432:5432" # <- Vite でフロントエンドの開発サーバーも起動する想定
# ...
mysql:
image: mysql
# ...
localstack:
image: localstack/localstack
# ...
mailpit:
image: axllent/mailpit
ports:
- "8025:8025" # Web UI
# ...
公開ポートが半分に減りました。また LocalStack は、 AWS CLI から API の実行はしたい時は次の方法で可能です:
docker run -it --rm --network [ここにネットワーク名] \
-e AWS_ACCESS_KEY_ID=dummy \
-e AWS_SECRET_ACCESS_KEY=dummy \
amazon/aws-cli --endpoint-url http://localstack:4566/ s3 ls
--network
オプションで Compose で使われているネットワーク名を指定すると、その上で AWS CLI のコンテナを起動できるので localstack:4566
で接続可能になります。ネットワーク名はプロジェクト毎に異なるので docker network ls
で探してみてください。

2. 必要な場合でも .env
で可変にしておく
ポート公開が避けられない場合も、固定数字をハードコードしない だけで衝突リスクは激減します。Laravel Sail なんかはこの手法を使っていますね。
Docker Compose はデフォルトで .env
の中に定義されている環境変数が設定ファイルの中で有効になります。以下は .env
と compose.yaml
の設定例です:
# .env
APP_PORT=12000 # Rails
VITE_PORT=12001 # Vite Dev Server
FOWARD_DB_PORT=12002 # MySQL
FOWARD_MAILPIT_UI_PORT=12003 # Mailpit Web UI
# compose.yaml
services:
rails:
build:
context: .
command: bundle exec rails server -b 0.0.0.0
ports:
- "${APP_PORT:-3000}:3000"
- "${VITE_PORT:-5432}:5432"
# ...
mysql:
image: mysql
ports:
- "${FORWARD_MYSQL_PORT:-3306}:3000"
localstack:
image: localstack/localstack
# ...
mailpit:
image: axllent/mailpit
ports:
- "${FOWARD_MAILPIT_UI_PORT:-8025}:8025"
ポイントは、${APP_PORT:-3000}
の様にデフォルト値 (この場合 3000
) を定義する事で .env
に定義がない場合でも問題なく動作する点です。
これで自由に公開するポートを設定できる様になりました。ただ他のメンバーと URL を共有する場合はポートが異なる可能性を考慮する必要があります。
番外編
実は次の様にしてホスト側のポートを省略すると、 Docker が適当に使えるポートを使って公開してくれます:
mailpit:
image: axllent/mailpit
ports:
- "8025"
ただ多くの場合は固定した方が利便性が高いと思うので .env
を使うのが良いと思います。
まとめ
- まずは公開不要かどうかを真面目に考える
内部通信だけなら同一ネットワークで十分。 - 必要なら
.env
で柔軟に
ポート番号をコードから追い出せば、競合は最小化できる。
コメントを残す