Cách Sửa Lỗi 'EADDRINUSE: address already in use' Khi Khởi Động Server Node.js

beginner💚 Node.js2026-03-18| Node.js (tất cả phiên bản), macOS / Linux / Windows

Error Message

Error: listen EADDRINUSE: address already in use :::3000
#Node.js#EADDRINUSE#port

Mô Tả Lỗi

Error: listen EADDRINUSE: address already in use :::3000
    at Server.setupListenHandle [as _listen2] (node:net:1739:16)
    at listenInCluster (node:net:1787:12)
    at Server.listen (node:net:1875:7)
    at Function.listen (/app/node_modules/express/lib/application.js:635:24)

Port 3000 đang bị chiếm. Node.js cố gắng lắng nghe trên port đó, phát hiện có thứ gì đó đang chạy và dừng lại. Hai process không thể dùng chung một TCP port — vì vậy một trong số chúng phải bị dừng.

Nguyên Nhân

Ba nguyên nhân phổ biến:

  • Một server instance trước đó bị crash hoặc bạn đóng cửa sổ terminal đột ngột. Process Node.js vẫn tiếp tục chạy ngầm, giữ nguyên port 3000.
  • Thứ gì đó đã chiếm port đó rồi — một ứng dụng Node.js khác, Webpack dev server, ứng dụng Python Flask, v.v.
  • Bạn khởi động lại quá nhanh. Hệ điều hành cần một khoảng thời gian (thường vài giây) để thu hồi port sau khi process cũ thoát. Nhấn Ctrl+C rồi chạy npm start ngay lập tức? Bạn có thể đã không kịp chờ.

Cách Sửa Từng Bước

Bước 1: Tìm process đang chiếm port

Trên macOS / Linux:

lsof -i :3000

Ví dụ kết quả:

COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
node     1234  alice   23u  IPv6  12345      0t0  TCP *:3000 (LISTEN)

Cột PID1234 trong ví dụ này — là thứ bạn cần ở bước tiếp theo.

Muốn dùng ss hoặc netstat? Cũng được:

# Dùng ss
ss -tlnp | grep :3000

# Dùng netstat
netstat -tlnp | grep :3000

Trên Windows (Command Prompt hoặc PowerShell):

netstat -ano | findstr :3000

Ví dụ kết quả:

TCP    0.0.0.0:3000    0.0.0.0:0    LISTENING    5678

Số cuối cùng (5678) chính là PID.

Bước 2: Kill process

Trên macOS / Linux:

kill -9 1234

Thay 1234 bằng PID thực tế bạn tìm được. Hoặc bỏ qua bước tìm kiếm và làm gọn trong một lệnh:

kill -9 $(lsof -ti :3000)

Trên Windows (chạy với quyền Administrator):

taskkill /PID 5678 /F

Bước 3: Khởi động lại server Node.js

node app.js
# hoặc
npm start
# hoặc
npx ts-node src/index.ts

Cách Khắc Phục Khác: Dùng Port Khác

Có thể process đang dùng port 3000 là cố ý — một ứng dụng khác bạn cần chạy. Không sao. Chỉ cần chuyển server sang port khác.

Trong code ứng dụng của bạn:

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
  console.log(`Server đang chạy trên port ${PORT}`);
});

Hoặc đặt port khi khởi động mà không cần sửa code:

PORT=3001 node app.js

Trên Windows:

set PORT=3001 && node app.js

Sửa Lỗi Với nodemon / Chế Độ Watch Khi Phát Triển

Gặp lỗi này sau mỗi lần lưu file? nodemon có thể đang khởi động lại process trước khi hệ điều hành kịp thu hồi port. Thêm độ trễ 1 giây thường giải quyết được:

# nodemon.json
{
  "delay": 1000
}

Nếu vẫn bị kẹt, hãy kill toàn bộ process Node và khởi động lại từ đầu:

# macOS / Linux — kill tất cả process Node
pkill -f node

# sau đó khởi động lại
npm run dev

Kiểm Tra Sau Khi Sửa

Chạy lại lệnh tìm kiếm sau khi đã kill process:

# macOS / Linux
lsof -i :3000

# Windows
netstat -ano | findstr :3000

Không có kết quả nghĩa là port đã trống. Khởi động server — nó sẽ chạy bình thường.

Muốn xác nhận server đang phản hồi không? Thử curl nhanh:

curl http://localhost:3000
# hoặc mở http://localhost:3000 trên trình duyệt

Mẹo Tránh Lỗi Này

  • Ctrl+C tốt hơn đóng terminal — đóng cửa sổ terminal thường để lại process Node.js mồ côi. Nó vẫn chạy, vẫn giữ port. Ctrl+C gửi tín hiệu SIGINT và tắt đúng cách.
  • Đọc port từ biến môi trường — hardcode 3000 ổn cho đến khi nó không ổn nữa. process.env.PORT || 3000 cho phép bạn đổi port bằng một biến môi trường duy nhất, không cần sửa code.
  • Thêm graceful shutdown handler trong môi trường production — giúp server giải phóng port đúng cách khi thoát:
process.on('SIGTERM', () => {
  server.close(() => {
    console.log('Server đã đóng');
    process.exit(0);
  });
});
  • Dùng PM2? Chú ý instance cũpm2 restart app tự xử lý việc chuyển tiếp. Để khởi động sạch, hãy chạy pm2 stop all trước để đảm bảo không còn gì đang chiếm port của bạn.

Related Error Notes