DeployEasy
DockerCơ bản

Docker Port Is Already Allocated: Cách tìm tiến trình chiếm cổng trên Windows và Linux

Hướng dẫn xử lý port is already allocated, address already in use và EADDRINUSE trong Docker Compose mà không tắt nhầm tiến trình.

· 6 phút đọc· 1.255 từ
Mục lục bài viết

Khi Docker Compose khởi động container, Docker phải bind một cổng trên máy host. Nếu cổng đó đã được ứng dụng khác hoặc container khác sử dụng, bạn sẽ gặp lỗi như:

Bind for 0.0.0.0:8080 failed: port is already allocated

Trên Windows, thông báo có thể dài hơn:

ports are not available: exposing port TCP 127.0.0.1:8080:
listen tcp4 127.0.0.1:8080: bind: An attempt was made to access a socket...

Node.js chạy ngoài Docker có thể báo:

Error: listen EADDRINUSE: address already in use :::3000

Ba thông báo khác nhau nhưng đều đặt ra một câu hỏi: tiến trình nào đang giữ cổng host và mình nên dừng nó hay đổi cổng?

Cách Docker ánh xạ cổng host và container

1. Hiểu đúng 8080:3000

Trong Compose:

services:
  web:
    ports:
      - "8080:3000"

Ý nghĩa:

HOST_PORT:CONTAINER_PORT
8080     :3000

Trình duyệt gọi localhost:8080. Docker chuyển request đến cổng 3000 bên trong container.

Hai container có thể cùng nghe cổng 3000 bên trong vì chúng nằm trong network namespace riêng. Nhưng chúng không thể cùng chiếm một host port 8080 trên cùng địa chỉ bind.

2. Kiểm tra container Docker đang chiếm cổng

Bắt đầu bằng:

docker ps --format 'table {{.Names}}\t{{.Ports}}\t{{.Status}}'

Ví dụ:

NAMES          PORTS                         STATUS
old-web        0.0.0.0:8080->3000/tcp        Up 2 hours
postgres-dev   127.0.0.1:5432->5432/tcp      Up 2 hours

Container old-web đang giữ cổng 8080. Nếu đó là stack cũ không còn dùng:

docker stop old-web
docker rm old-web

Nếu container được tạo bởi Compose, ưu tiên vào đúng thư mục dự án rồi dùng:

docker compose down

Cách này dọn đúng container và network thuộc project, thay vì xóa thủ công từng thành phần.

3. Tìm tiến trình chiếm cổng trên Linux

Dùng ss:

sudo ss -lntp | grep ':8080'

Hoặc lsof nếu đã cài:

sudo lsof -nP -iTCP:8080 -sTCP:LISTEN

Ví dụ:

LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("docker-proxy",pid=2145,fd=4))

Nếu là docker-proxy, quay lại docker ps để tìm container. Nếu là node, nginx, java hoặc process khác, xem đầy đủ thông tin trước khi dừng:

ps -fp 2145
sudo systemctl status <ten-service>

Không nên chạy kill -9 ngay. Hãy thử dừng đúng service hoặc gửi tín hiệu kết thúc bình thường trước.

Terminal demo tìm và giải phóng cổng

4. Tìm PID chiếm cổng trên Windows

Trong PowerShell hoặc Command Prompt:

netstat -ano | findstr :8080

Ví dụ:

TCP    127.0.0.1:8080    0.0.0.0:0    LISTENING    14620

Tra tiến trình:

tasklist /FI "PID eq 14620"

PowerShell hiện đại cũng có thể dùng:

Get-NetTCPConnection -LocalPort 8080 -State Listen |
  Select-Object LocalAddress, LocalPort, OwningProcess

Get-Process -Id 14620

Nếu PID thuộc Docker Desktop, hãy kiểm tra container bằng docker ps trước. Dừng Docker Desktop chỉ để giải phóng một cổng thường là giải pháp quá mạnh và làm gián đoạn tất cả container.

5. Ba cách xử lý đúng

Cách 1: Dừng tiến trình cũ

Dùng khi tiến trình cũ không còn cần thiết.

Linux service:

sudo systemctl stop my-old-app

Node.js chạy thủ công:

kill <PID>

Docker Compose:

docker compose -p old-project down

Sau đó xác minh:

sudo ss -lntp | grep ':8080' || echo 'Port 8080 is free'

Cách 2: Đổi host port

services:
  web:
    ports:
      - "8081:3000"

Khởi động lại:

docker compose up -d --force-recreate

Truy cập http://localhost:8081.

Đây là lựa chọn tốt khi bạn cần chạy đồng thời nhiều phiên bản, ví dụ production giả lập và development.

Cách 3: Không publish port không cần thiết

Nếu API chỉ được frontend hoặc Nginx trong cùng Compose network gọi, bạn có thể không cần đưa port ra host:

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"

  api:
    build: ./api
    expose:
      - "3000"

Nginx trong container gọi:

proxy_pass http://api:3000;

expose giúp mô tả cổng nội bộ nhưng không publish nó ra host. Trong cùng Compose network, các service vẫn có thể gọi nhau bằng tên service và cổng container.

6. Bind vào 127.0.0.1 hay 0.0.0.0?

ports:
  - "127.0.0.1:3001:3000"

Cấu hình trên chỉ publish cổng tại loopback của host. Nó phù hợp khi Nginx trên cùng VPS sẽ proxy đến app và bạn không muốn người ngoài truy cập trực tiếp cổng 3001.

ports:
  - "3001:3000"

Thông thường Docker bind lên tất cả interface host. Cổng có thể truy cập từ mạng bên ngoài nếu firewall cho phép.

Không nên mở mọi cổng ra Internet chỉ vì thấy website chưa truy cập được. Hãy xác định rõ service nào cần public.

7. Trường hợp Windows báo cổng bị cấm dù không thấy PID

Đôi khi Windows giữ một dải cổng cho hệ thống hoặc tính năng ảo hóa. Bạn có thể kiểm tra các dải TCP được loại trừ:

netsh interface ipv4 show excludedportrange protocol=tcp

Nếu cổng nằm trong dải bị loại trừ, cách an toàn nhất thường là chọn host port khác, ví dụ 8081 hoặc 18080. Không nên chỉnh dải cổng hệ thống một cách tùy tiện vì có thể ảnh hưởng Hyper-V, WSL hoặc Docker Desktop.

8. Tránh xung đột trong nhiều dự án Compose

Dùng port theo quy ước

Ví dụ:

Project A web: 3101
Project A api: 3102
Project B web: 3201
Project B api: 3202

Đặt tên project rõ ràng

docker compose -p deployeasy-dev up -d

Xem project đang chạy:

docker compose ls

Không đặt container_name khi không cần

Compose tự tạo tên dựa trên project và service. Việc hard-code container_name dễ gây xung đột khi chạy nhiều bản sao của cùng một stack.

9. Quy trình xử lý nhanh

Linux:

docker ps --format 'table {{.Names}}\t{{.Ports}}'
sudo ss -lntp | grep ':8080'
ps -fp <PID>
docker compose down   # chỉ khi đúng project

Windows:

docker ps --format "table {{.Names}}\t{{.Ports}}"
netstat -ano | findstr :8080
tasklist /FI "PID eq <PID>"

Sau đó chọn một trong ba hướng: dừng tiến trình cũ, đổi host port hoặc bỏ publish port không cần thiết.

10. Checklist xác minh

  • Xác định đúng host port đang xung đột.
  • Tìm container hoặc PID sở hữu cổng.
  • Không dừng nhầm database hay service production.
  • Cập nhật ports nếu cần chạy song song.
  • Chỉ publish các cổng thực sự cần truy cập từ host.
  • Chạy lại docker compose up -d.
  • Kiểm tra bằng docker compose pscurl.

11. Câu hỏi thường gặp

Vì sao đổi 3000 bên phải mà lỗi vẫn còn?

Cổng xung đột thường nằm bên trái. Với 8080:3000, host đang giữ cổng 8080. Đổi container port không giải phóng host port đó.

docker compose down -v có nên dùng không?

Không dùng chỉ để sửa lỗi cổng. Tùy cấu hình, -v có thể xóa volume chứa database. docker compose down thường đã đủ để dừng container và giải phóng port.

Hai project có thể cùng dùng cổng container 3000 không?

Có. Mỗi container có network namespace riêng. Chúng chỉ xung đột khi cùng publish vào một host port.

Nguồn tham khảo

Đọc tiếp