Chuyện gì xảy ra
App của bạn vừa crash với lỗi:
Error: connect ECONNREFUSED 127.0.0.1:5432
ECONNREFUSED có nghĩa là hệ điều hành đã gõ vào cổng đó và bị đóng sầm trước mặt. Không phải timeout. Không phải firewall drop. Đây là từ chối chủ động — không có gì đang lắng nghe trên cổng đó cả.
Số cổng cho bạn biết service nào đã "bốc hơi": 5432 = PostgreSQL, 3306 = MySQL, 6379 = Redis, 27017 = MongoDB. Nguyên nhân gốc rễ đều giống nhau — service chưa chạy, hoặc app đang trỏ sai địa chỉ.
Quy trình debug
Bước 1 — Service có đang chạy không?
Chín trên mười trường hợp là vì lý do này. Kiểm tra ở đây trước khi đụng vào bất cứ thứ gì khác.
# Linux / macOS
sudo systemctl status postgresql
sudo systemctl status mysql
sudo systemctl status redis
# Hoặc kiểm tra xem cổng đó đang được lắng nghe bởi gì
sudo ss -tlnp | grep 5432
sudo lsof -i :5432
Không có output cho cổng đó? Service đang down. Chuyển xuống Fix 1 bên dưới.
Bước 2 — Service có đang lắng nghe đúng interface không?
Phức tạp hơn bạn nghĩ. PostgreSQL có thể đang chạy bình thường nhưng chỉ bind vào 127.0.0.1 — nghĩa là nó từ chối mọi kết nối không đến từ localhost.
sudo ss -tlnp | grep 5432
# Output mong đợi:
# LISTEN 0 128 127.0.0.1:5432 0.0.0.0:*
0.0.0.0:5432 hoặc :::5432 nghĩa là chấp nhận kết nối trên tất cả interface. Chỉ thấy 127.0.0.1:5432? Kết nối từ xa không thể thực hiện được — chỉ dùng local thôi.
Bước 3 — Kiểm tra connection string trong app
Lỗi đánh máy vẫn xảy ra. Chỉ một ký tự sai trong DB_HOST hay DB_PORT là mọi kết nối đều rơi vào hố đen.
# Node.js — kiểm tra file .env hoặc config
DB_HOST=localhost
DB_PORT=5432
# Python SQLAlchemy
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
Một điều hay bị bỏ sót: localhost trên máy này có thực sự phân giải ra 127.0.0.1 không?
ping localhost
# Phải phân giải ra 127.0.0.1 hoặc ::1
Trên WSL2 hay Docker, localhost thường phân giải khác với những gì bạn mong đợi — điều này đã hại không ít developer rồi.
Bước 4 — Dùng Docker? Network mới là vấn đề
Bên trong Docker container, localhost chính là container đó — không phải laptop hay VM của bạn. Database trên máy host không thể truy cập qua địa chỉ đó.
# Linux — dùng IP của Docker bridge gateway
DB_HOST=172.17.0.1
# Docker Desktop trên Mac hoặc Windows — dùng cái này là xong
DB_HOST=host.docker.internal
Hai container cần giao tiếp với nhau? Đặt chúng cùng một Docker network có tên và dùng tên container làm hostname. Đừng dùng địa chỉ IP — chúng thay đổi mỗi khi restart.
Cách fix
Fix 1 — Khởi động service
# PostgreSQL
sudo systemctl start postgresql
sudo systemctl enable postgresql # tự khởi động cùng hệ thống
# MySQL / MariaDB
sudo systemctl start mysql
# Redis
sudo systemctl start redis
Sau khi khởi động, xác nhận service đã thực sự lên:
sudo ss -tlnp | grep 5432
Fix 2 — Service crash khi khởi động, kiểm tra log
systemctl start có thể thất bại âm thầm — lệnh chạy xong không báo lỗi, nhưng service lại chết sau vài giây. Log sẽ cho bạn biết lý do tại sao.
journalctl -u postgresql -n 50 --no-pager
# Tìm kiếm: lỗi phân quyền, xung đột cổng, lỗi cú pháp config
Ba nghi phạm thường gặp: đĩa đầy (chạy df -h), sai quyền sở hữu thư mục data (/var/lib/postgresql/), hoặc một tiến trình khác đã chiếm cổng 5432.
# Tìm xem gì đang chiếm cổng của bạn
sudo lsof -i :5432
Fix 3 — Thay đổi bind address để nhận kết nối từ xa
Với PostgreSQL, tìm và sửa file postgresql.conf:
# Tìm vị trí file config
psql -U postgres -c 'SHOW config_file;'
# Sau đó sửa dòng này:
listen_addresses = '*' # trước đó là: 'localhost'
Đừng quên cập nhật cả pg_hba.conf để whitelist host từ xa, rồi restart:
sudo systemctl restart postgresql
Với MySQL, cài đặt này nằm trong /etc/mysql/mysql.conf.d/mysqld.cnf:
bind-address = 0.0.0.0 # trước đó là: 127.0.0.1
Fix 4 — Biến môi trường chưa được load
Lỗi thời điểm kinh điển: app khởi động trước khi .env được load, fallback về giá trị mặc định như localhost:5432, và kết nối vào không khí. Hãy in ra những gì app thực sự thấy lúc runtime:
# 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'))
Thấy undefined hay None? File env chưa được load — kiểm tra lại cấu hình dotenv hoặc cách tiến trình đang được khởi động.
Xác minh
Trước khi restart app, hãy test TCP thô để xác nhận cổng thực sự có thể truy cập:
# Kiểm tra kết nối TCP trực tiếp
nc -zv 127.0.0.1 5432
# Thành công: Connection to 127.0.0.1 5432 port [tcp/postgresql] succeeded!
# Cách thay thế với curl
curl -v telnet://127.0.0.1:5432
Test này thành công mà app vẫn lỗi? Vấn đề nằm ở config của app, không phải service. Quay lại Bước 3.
Mẹo hay
Đang debug Docker bridge network hay VPN routing và nhìn chằm chằm vào một địa chỉ IP lạ hoắc? Subnet Calculator trên ToolCraft giúp bạn kiểm tra nhanh địa chỉ IP thuộc dải mạng nào — hữu ích để xác nhận địa chỉ đó là local hay đang bị route sang nơi khác.
Bài học rút ra
- ECONNREFUSED = không có gì lắng nghe. Debug service trước, đừng đụng vào code app.
- Chạy
ss -tlnphoặclsof -i :PORTtrước khi làm bất cứ điều gì. Hai giây, tiết kiệm hai mươi phút. - Cái bẫy
localhostcủa Docker thì ai cũng dính ít nhất một lần —host.docker.internallà cách fix trên Mac và Windows. - Thêm health check lúc khởi động để app chờ database sẵn sàng thay vì crash ngay. Tool như wait-for-it xử lý được trong một dòng lệnh.
- Chạy
systemctl enablecho mọi database service. Reboot server không có nghĩa là phải đi tìm lại lỗi này từ đầu.

