何が起きたのか
アプリが突然こんなエラーで落ちた:
Error: connect ECONNREFUSED 127.0.0.1:5432
ECONNREFUSED は、OSがそのポートをノックしたら扉を思いっきり閉められた状態だ。タイムアウトでもない。ファイアウォールによるドロップでもない。積極的な拒否——そのポートで何も待ち受けていない。
ポート番号を見れば、どのサービスが音信不通になったかがわかる:5432 = PostgreSQL、3306 = MySQL、6379 = Redis、27017 = MongoDB。どれも根本原因は同じ——サービスが起動していないか、アプリが間違ったアドレスを指しているかだ。
デバッグの手順
ステップ 1 — サービスは本当に起動しているか?
十中八九これが原因だ。他に何もする前にここから始めよう。
# Linux / macOS
sudo systemctl status postgresql
sudo systemctl status mysql
sudo systemctl status redis
# または実際にそのポートで待ち受けているものを確認する
sudo ss -tlnp | grep 5432
sudo lsof -i :5432
そのポートの出力がない?サービスが落ちている。後述の修正1に飛ぼう。
ステップ 2 — 正しいインターフェースで待ち受けているか?
見た目より厄介な問題だ。PostgreSQLは正常に起動していても、127.0.0.1 にしかバインドされていない場合がある——つまりローカルホストからの接続しか受け付けない。
sudo ss -tlnp | grep 5432
# 期待される出力:
# LISTEN 0 128 127.0.0.1:5432 0.0.0.0:*
0.0.0.0:5432 または :::5432 なら全インターフェースで接続を受け付けている。127.0.0.1:5432 だけ?リモート接続は最初から死んでいる——ローカルのみだ。
ステップ 3 — アプリの接続文字列を確認する
タイポは起きる。DB_HOST や DB_PORT の一文字の間違いで、すべての接続試行がブラックホールに吸い込まれる。
# Node.js — .env または設定ファイルを確認する
DB_HOST=localhost
DB_PORT=5432
# Python SQLAlchemy
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
見落としがちな点:このマシンで localhost は本当に 127.0.0.1 に解決されるか?
ping localhost
# 127.0.0.1 または ::1 に解決されるはず
WSL2やDockerでは、localhost が期待と異なる解決をすることがよくある——多くの開発者がこれに引っかかっている。
ステップ 4 — Docker?おそらくネットワークの問題だ
Dockerコンテナの内部では、localhost はコンテナ自身を指す——ラップトップでもVMでもない。ホストマシン上のデータベースはそのアドレスでは到達できない。
# Linux — DockerブリッジゲートウェイのIPを使う
DB_HOST=172.17.0.1
# MacまたはWindows上のDocker Desktop — これで動く
DB_HOST=host.docker.internal
2つのコンテナが通信する必要がある?同じ名前付きDockerネットワークに置いて、コンテナ名をホスト名として使おう。IPアドレスに頼ってはいけない——再起動のたびに変わる。
修正方法
修正 1 — サービスを起動する
# PostgreSQL
sudo systemctl start postgresql
sudo systemctl enable postgresql # 起動時に自動スタート
# MySQL / MariaDB
sudo systemctl start mysql
# Redis
sudo systemctl start redis
起動後、実際に立ち上がったか確認する:
sudo ss -tlnp | grep 5432
修正 2 — 起動時にクラッシュしている場合はログを確認する
systemctl start はサイレントに失敗することがある——コマンドはエラーなしで返るが、サービスは数秒以内に死ぬ。ログが理由を教えてくれる。
journalctl -u postgresql -n 50 --no-pager
# 探すもの: パーミッションエラー、ポートの競合、設定ファイルの構文エラー
よくある3つの原因:ディスクがフル(df -h で確認)、データディレクトリ(/var/lib/postgresql/)の所有権が間違っている、または別のプロセスがすでにポート5432を掴んでいる。
# ポートを占有しているものを見つける
sudo lsof -i :5432
修正 3 — リモート接続のためにバインドアドレスを変更する
PostgreSQLの場合、postgresql.conf を見つけて編集する:
# 設定ファイルの場所を確認する
psql -U postgres -c 'SHOW config_file;'
# この行を変更する:
listen_addresses = '*' # 元: 'localhost'
pg_hba.conf も更新してリモートホストをホワイトリストに追加することを忘れずに。その後再起動する:
sudo systemctl restart postgresql
MySQLの場合、設定は /etc/mysql/mysql.conf.d/mysqld.cnf にある:
bind-address = 0.0.0.0 # 元: 127.0.0.1
修正 4 — 環境変数が読み込まれていない
よくあるタイミングの問題:アプリが .env が読み込まれる前に起動し、localhost:5432 のようなデフォルト値にフォールバックして何にも繋がらない。実行時にアプリが実際に何を見ているか出力してみよう:
# Node.js
console.log('DB:', process.env.DB_HOST, process.env.DB_PORT);
# Python
import os
print(os.getenv('DB_HOST'), os.getenv('DB_PORT'))
undefined や None が表示される?envファイルが読み込まれていない——dotenvの設定やプロセスの起動方法を確認しよう。
確認方法
アプリを再起動する前に、生のTCPテストでポートが実際に到達可能か確認しよう:
# TCP接続を直接テストする
nc -zv 127.0.0.1 5432
# 成功: Connection to 127.0.0.1 5432 port [tcp/postgresql] succeeded!
# curlを使った代替方法
curl -v telnet://127.0.0.1:5432
これが成功してもアプリがまだ失敗する?問題はサービスではなくアプリの設定にある。ステップ3に戻ろう。
Tips
DockerブリッジネットワークやVPNルーティングをデバッグしていて、見覚えのないIPを眺めている?ToolCraftのサブネット計算機を使えば、あるIPがどのネットワーク範囲に属するかをすばやく確認できる——アドレスがローカルなのか予期しない場所にルーティングされているのかを確かめるのに便利だ。
得られた教訓
- **ECONNREFUSED = 何も待ち受けていない。**アプリのコードではなく、まずサービスをデバッグしよう。
- 何かに触れる前に
ss -tlnpまたはlsof -i :PORTを実行しよう。2秒で20分を節約できる。 - Dockerの
localhostトラップは誰もが一度は引っかかる——MacとWindowsでの修正はhost.docker.internalだ。 - 起動時のヘルスチェックを追加して、アプリがクラッシュする代わりにデータベースを待つようにしよう。wait-for-it のようなツールが一行でこれを解決してくれる。
- すべてのデータベースサービスで
systemctl enableを実行しよう。サーバーの再起動のたびにこのエラーを追いかけ回すことになってはいけない。

