はじめに
「昨日まで動いていたのに、別のプロジェクトを立ち上げたらエラーになった」
ローカル開発をしていると、一度は遭遇するこの問題。Address already in use というエラーメッセージに悩まされた経験がある方も多いのではないでしょうか。
この記事では、ポート被りの「今すぐ解決したい」から「二度と起こさない仕組み作り」まで、実践的な対策を紹介します。
この記事で解決できること
- 「Address already in use」エラーの即座の解消
- ポート番号の体系的な管理ルール
- Docker環境でのポート設定のベストプラクティス
- チーム開発での運用方法
対象読者
- ローカル開発環境を構築している方(初心者〜中級者)
- 複数プロジェクトを並行して開発している方
- Docker未経験でもOK
まずはエラーを解決する(今すぐ使えるコマンド集)
「理屈はいいから、今すぐ動かしたい!」という方のために、解決コマンドを先に紹介します。
エラーの原因
Address already in use エラーは、使用したいポートが既に別のプロセスに占有されている状態で発生します。よくある原因は以下の通りです。
- サーバーを停止せずにターミナルを閉じた
- 別のプロジェクトが同じポートで起動中
- 前回のプロセスがゾンビ化して残っている
参考:Address already in useのエラーを解決する - Qiita
ポートを使っているプロセスの特定
Mac / Linux の場合
# 指定したポートを使っているプロセスを特定
lsof -i :3000
# 出力例
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# node 12345 user 23u IPv4 0x1234 0t0 TCP *:3000 (LISTEN)
lsof は「List Open Files」の略で、どのプロセスがどのファイル(ポートを含む)を開いているかを表示するコマンドです。
参考:lsof Command in Linux with Examples - GeeksforGeeks
Windows の場合
# 指定したポートを使っているプロセスを特定
netstat -ano | findstr :3000
# 出力例
# TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 12345
一番右の数字(12345)がプロセスID(PID)です。
参考:Netstatコマンドとは。概要やオプション、その使い方を徹底解説!
プロセスの終了方法
Mac / Linux の場合
# 通常の終了(推奨)
kill 12345
# 強制終了(通常の終了で止まらない場合のみ)
kill -9 12345
kill -9 は強制終了オプションです。まずは -9 なしで試し、それでも終了しない場合のみ使用してください。
Windows の場合
# プロセスを終了
taskkill /PID 12345
# 強制終了
taskkill /PID 12345 /F
参考:特定のポート番号を使用しているプロセスを見つけ、終了する方法 - Qiita
ワンライナーで一気に解決(Mac / Linux)
毎回PIDを確認するのが面倒な場合、以下のワンライナーが便利です。
# ポート3000を使っているプロセスを強制終了
lsof -ti :3000 | xargs kill -9
よく使われるポート番号一覧
「なぜ3000?なぜ8080?」という疑問を持ったことはありませんか?ポート番号には慣習的な使い分けがあります。
ポート番号の分類
ポート番号は0〜65535の範囲で、IANAにより以下の3つに分類されています。
| 範囲 | 名称 | 用途 |
|---|---|---|
| 0〜1023 | システムポート(Well-known Ports) | HTTP(80)、HTTPS(443)など標準プロトコル用。管理者権限(root)が必要 |
| 1024〜49151 | 登録済みポート(Registered Ports) | 特定のアプリケーション用にIANAが管理 |
| 49152〜65535 | 動的/プライベートポート | 一時的な通信や開発用に自由に使用可能 |
参考:TCPやUDPにおけるポート番号の一覧 - Wikipedia
開発でよく使われるポート番号
| ポート | 用途 | 使用例 |
|---|---|---|
| 80 | HTTP(本番環境) | Apache, nginx |
| 443 | HTTPS(本番環境) | SSL/TLS通信 |
| 3000 | フロントエンド開発 | React, Next.js, Rails |
| 3306 | MySQL | データベース |
| 5173 | Vite | フロントエンド開発サーバー |
| 5432 | PostgreSQL | データベース |
| 8000 | バックエンド開発 | Django, FastAPI |
| 8080 | 代替HTTPポート | Tomcat, プロキシサーバー, Spring Boot |
| 9000 | 管理ツール系 | PHP-FPM, SonarQube |
参考:ポートの基礎知識とよく使われるポート番号のまとめ - Zenn、Server Options | Vite(公式)
なぜ8080がよく使われるのか?
8080は「代替HTTPポート」として広く認知されています。0〜1023のポート番号はLinuxでは管理者権限(root)が必要なため、開発環境では80番の代わりに8080を使うのが慣習となりました。
ポート被りを防ぐルール作り
ここからが本題です。エラーを「解決する」だけでなく「予防する」ためのルール作りを紹介します。
ポート管理のルール化(2つの方式)
複数プロジェクトを同時起動する場合、ポート競合を防ぐルールを決めておくと便利です。ここでは2つの方式を紹介します。
方式1: オフセット方式
各ツールのデフォルトポートに、プロジェクトごとの固定値を加算する方式です。
| ツール | デフォルト | プロジェクトA (+1) | プロジェクトB (+2) |
|---|---|---|---|
| React / Next.js | 3000 | 3001 | 3002 |
| Vite | 5173 | 5174 | 5175 |
| Django / FastAPI | 8000 | 8001 | 8002 |
| PostgreSQL | 5432 | 5433 | 5434 |
メリット: デフォルトポートに近いので、ドキュメントとの対応がわかりやすい
デメリット: 各ツールのデフォルトポートを覚えておく必要がある
参考:【ポート競合対策】個人開発でローカル環境のポート番号割り当ての工夫 - Zenn
方式2: ブロック方式(筆者はこちらを採用)
プロジェクトごとに「ポートの百の位」を固定し、末尾の数字を 3層アーキテクチャ に対応させる方式です。4000番台を使うことで、よく使われるデフォルトポート(3000, 3306, 5432, 8080など)との衝突を回避できます。
| プロジェクト | ポート範囲 | プレゼンテーション層 (0x) | アプリケーション層 (1x) | データ層 (2x) |
|---|---|---|---|---|
| A | 4100-4199 | 4100 | 4110 | 4120 |
| B | 4200-4299 | 4200 | 4210 | 4220 |
| C | 4300-4399 | 4300 | 4310 | 4320 |
末尾の割り当てルール:
| 層 | 末尾 | 役割 |
|---|---|---|
| プレゼンテーション層 | 00 | Frontend(メイン) |
| 01 | Storybook / Preview | |
| アプリケーション層 | 10 | Backend API |
| 11 | Admin API | |
| データ層 | 20 | PostgreSQL |
| 21 | MySQL | |
| 30 | Redis(キャッシュ) | |
| 補助ツール | 40 | Adminer / pgAdmin |
| 41 | MailHog等 |
メリット:
- 「プロジェクトAは41xx」と覚えるだけで済む
- ポート番号を見れば即座にプロジェクトと役割がわかる(4120 = A案件のDB)
- 十の位でアーキテクチャ層が判別できる(0x=画面、1x=API、2x-3x=データ)
- デフォルトポートを覚えている必要がない
- デフォルトポートと衝突しない
デメリット: デフォルトポートとの対応がわかりにくい
ブロック方式の可視化
ブロック方式なら「プロジェクトAは41xx」と覚えるだけで、十の位を見ればどの層のサービスかも即座にわかります。
環境変数(.env)での管理
コード内にポート番号をハードコードするのは避けましょう。環境変数で管理することで、競合時の変更が容易になります。
.env.example を用意する
プロジェクトのルートに .env.example を作成し、デフォルト値を記載します。
# .env.example
PORT=3000
API_PORT=8000
DB_PORT=5432
DATABASE_URL=postgres://localhost:5432/mydb
各自のローカルで .env を作成
# .env(.gitignoreに追加すること)
PORT=3005
API_PORT=8005
DB_PORT=5435
DATABASE_URL=postgres://localhost:5435/mydb
アプリケーション側での読み込み例
Node.js の場合
// dotenvを使用
require('dotenv').config();
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
Python (Django) の場合
import os
from dotenv import load_dotenv
load_dotenv()
PORT = int(os.getenv('PORT', 8000))
運用のポイント
-
.env.exampleはリポジトリにコミットする -
.envは.gitignoreに追加し、各自のローカルで管理 - 競合が発生したら
.envの値を変更するだけで対応可能
Dockerでのポート管理
Dockerを使用している場合、コンテナ内部のポートは固定し、ホスト側のポートだけをプロジェクトごとにずらすのが定石です。
基本的なポートマッピング
# docker-compose.yml
version: '3'
services:
web:
image: nginx:latest
ports:
- "8080:80" # ホストの8080 → コンテナの80
ホスト側ポート:コンテナ側ポート の形式で指定します。
参考:docker-compose の ports 指定まとめ - Qiita
プロジェクトごとにポートをずらす
ブロック方式(3層アーキテクチャ対応)を採用した場合の例です。
# プロジェクトA(41xx)の docker-compose.yml
version: '3'
services:
# プレゼンテーション層(0x)
web:
image: node:18
ports:
- "4100:3000"
# アプリケーション層(1x)
api:
image: python:3.11
ports:
- "4110:8000"
# データ層(2x)
db:
image: postgres:15
ports:
- "4120:5432"
# プロジェクトB(42xx)の docker-compose.yml
version: '3'
services:
web:
image: node:18
ports:
- "4200:3000"
api:
image: python:3.11
ports:
- "4210:8000"
db:
image: postgres:15
ports:
- "4220:5432"
環境変数と組み合わせる
ポート番号を .env ファイルで管理すると、より柔軟になります。
# docker-compose.yml
version: '3'
services:
web:
image: node:18
ports:
- "${WEB_PORT:-4100}:3000"
db:
image: postgres:15
ports:
- "${DB_PORT:-4120}:5432"
# .env(プロジェクトAの場合)
WEB_PORT=4100
DB_PORT=4120
ローカル専用にする(セキュリティ対策)
外部からのアクセスを防ぐため、ホスト側のIPを 127.0.0.1 に限定できます。
ports:
- "127.0.0.1:3000:3000"
参考:Dockerでサーバー構築する際のポート指定で気をつけること - UCWD-Studio
チーム開発での運用Tips
ポート管理表を作成する
チームで開発する場合、ポート割り当てを一覧化しておくと便利です。ブロック方式を採用した場合の例:
| プロジェクト | 範囲 | Frontend (0x) | Backend (1x) | Database (2x) | 備考 |
|---|---|---|---|---|---|
| ECサイト | 41xx | 4100 | 4110 | 4120 | 本番リリース済み |
| 管理画面 | 42xx | 4200 | 4210 | 4220 | 開発中 |
| モバイルAPI | 43xx | - | 4310 | 4320 | API専用 |
READMEに書くべき情報
プロジェクトのREADMEには、最低限以下を記載しましょう。
## 開発環境
### 使用ポート(ブロック方式: 41xx)
| 層 | サービス | ポート |
|:--|:--|:--|
| プレゼンテーション | Frontend | 4100 |
| アプリケーション | Backend API | 4110 |
| データ | PostgreSQL | 4120 |
### 起動方法
1. `.env.example` を `.env` にコピー
2. `docker-compose up -d`
3. http://localhost:4100 にアクセス
### ポート競合時の対処
`.env` ファイルの `PORT` を変更してください。
まとめ
ローカル開発でのポート被りを防ぐために、最低限やるべき3つのことをまとめます。
1. エラー時の対処法を覚える
# Mac/Linux: ポートを使っているプロセスを確認
lsof -i :3000
# Windows: ポートを使っているプロセスを確認
netstat -ano | findstr :3000
2. ポート番号は環境変数で管理する
# .env
PORT=3000
コードにハードコードしない。これだけで競合時の対応が楽になります。
3. プロジェクトごとにポート管理ルールを決める
「プロジェクトAは41xx」のようにブロック方式でルール化すると、ポート番号を見ただけでどのプロジェクトかわかります。さらに十の位を3層アーキテクチャに対応させれば(0x=画面、1x=API、2x=DB)、どの層のサービスかも一目瞭然です。
参考資料
- Address already in useのエラーを解決する - Qiita
- 特定のポート番号を使用しているプロセスを見つけ、終了する方法 - Qiita
- lsof Command in Linux with Examples - GeeksforGeeks
- ポートの基礎知識とよく使われるポート番号のまとめ - Zenn
- 【ポート競合対策】個人開発でローカル環境のポート番号割り当ての工夫 - Zenn
- TCPやUDPにおけるポート番号の一覧 - Wikipedia
- root権限の管理 - LinuC
- Server Options | Vite(公式)
- docker-compose の ports 指定まとめ - Qiita
- Docker Compose を使用したローカル開発 - Heroku
- Netstatコマンドとは - Winserver