Sửa lỗi 'Bind for 0.0.0.0:80 failed: port is already allocated' trong Docker

beginner🐳 Docker2026-03-17| Docker Engine 20+, Docker Compose v2, Linux/macOS/Windows (WSL2)

Error Message

Bind for 0.0.0.0:80 failed: port is already allocated
#docker#port#bind

Tình huống xảy ra lỗi

Bạn chạy docker compose up hoặc docker run -p 80:80 ... và nhận được thông báo lỗi:

Error response from daemon: driver failed programming external connectivity on endpoint my_container:
Bind for 0.0.0.0:80 failed: port is already allocated

Có thứ gì đó đã chiếm cổng 80 trước Docker. Docker không thể giành lại được, nên container của bạn không thể khởi động.

Nguyên nhân thực sự

Khi Docker ánh xạ một cổng, nó tạo một binding trên máy host tại 0.0.0.0:80 và chuyển tiếp traffic vào container. Chỉ một tiến trình duy nhất có thể giữ binding đó tại một thời điểm. Nếu có thứ gì đó chiếm trước — một container khác, nginx cài trên hệ thống, hay một dev server bị bỏ quên — Docker sẽ thất bại và ném ra lỗi này.

Các nguyên nhân thường gặp:

  • Một container Docker trước đó không dừng sạch (SIGKILL, OOM kill, mất điện)
  • Nginx hoặc Apache được cài trực tiếp trên máy host
  • Một project docker compose khác đang chạy trên cổng 80
  • Một dev server Node.js hoặc Python vẫn đang chạy ở terminal khác

Bước 1 — Tìm tiến trình đang dùng cổng

Trên Linux / macOS

sudo ss -tlnp | grep :80
# hoặc
sudo lsof -i :80

Ví dụ output từ ss:

LISTEN  0  128  0.0.0.0:80  0.0.0.0:*  users:(("nginx",pid=1234,fd=6))

Từ lsof:

COMMAND   PID     USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
nginx    1234     root    6u  IPv4   12345      0t0  TCP *:http (LISTEN)

Cả hai đều cho bạn biết tên tiến trình và PID. Đó là tất cả những gì bạn cần.

Trên Windows (PowerShell)

netstat -ano | findstr :80

Sau đó tra cứu PID:

tasklist | findstr <PID>

Kiểm tra xem có phải container Docker khác không

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

Xem cột Ports để tìm bất kỳ thứ gì được ánh xạ tới :80.

Bước 2 — Khắc phục nhanh

Nếu là container Docker

# Dừng một container cụ thể
docker stop <container_name_or_id>

# Hoặc dừng tất cả container đang chạy
docker stop $(docker ps -q)

Sau đó chạy lại lệnh ban đầu của bạn.

Nếu là service hệ thống (nginx, apache)

# Linux — systemd
sudo systemctl stop nginx
sudo systemctl stop apache2

# macOS — brew services
brew services stop nginx

Nếu là tiến trình ngẫu nhiên

# Kill theo PID từ output của lsof/ss
sudo kill -9 <PID>

Bước 3 — Xác nhận cổng đã được giải phóng

sudo ss -tlnp | grep :80

Không có output? Cổng đã trống. Khởi động container của bạn:

docker compose up -d

Xác nhận đã binding đúng:

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

Output mong đợi:

NAMES       STATUS         PORTS
my_app      Up 3 seconds   0.0.0.0:80->80/tcp

Giải pháp lâu dài — Ngăn xung đột tái diễn

Phương án A: Đổi cổng host trong file compose

Cổng 80 trên host không phải là bất khả xâm phạm. Ánh xạ sang 8080 thay vào đó cho đơn giản:

services:
  app:
    image: nginx
    ports:
      - "8080:80"  # Host 8080 → Container 80

Truy cập tại http://localhost:8080. Với môi trường production đặt sau load balancer, cổng host hầu như không quan trọng.

Phương án B: Tắt vĩnh viễn service hệ thống đang xung đột

# Dừng nginx và không cho khởi động cùng hệ thống
sudo systemctl disable --now nginx

# Chỉ dừng tạm thời mà không vô hiệu hóa
sudo systemctl stop nginx

Phương án C: Đặt một reverse proxy đứng trước tất cả

Đang chạy nhiều service đều muốn dùng cổng 80? Đừng expose chúng trực tiếp. Một proxy container duy nhất chiếm cổng 80 và định tuyến traffic nội bộ — mọi thứ còn lại không cần tiếp xúc với mạng host.

services:
  proxy:
    image: traefik:v3
    ports:
      - "80:80"
  app:
    image: my-app
    # Không expose cổng — traffic đi qua proxy

Trường hợp đặc biệt: Cổng hiển thị trống nhưng Docker vẫn báo lỗi

Điều này thường khiến mọi người bất ngờ. Một container đã dừng vẫn có thể giữ nguyên port binding — đặc biệt sau khi bị kill cứng hoặc sự kiện OOM. Cách khắc phục là xóa hoàn toàn container, không chỉ dừng nó:

# Liệt kê tất cả container kể cả đã dừng
docker ps -a

# Xóa container bị kẹt
docker rm <container_name_or_id>

Sau khi tắt không sạch với Docker Compose, bỏ qua bước dọn dẹp thủ công và chạy luôn:

docker compose down
docker compose up -d

compose down gỡ bỏ network và xóa binding đúng cách. Lệnh docker stop đơn thuần bỏ qua bước đó, đó là lý do binding ma vẫn còn tồn tại.

Tóm tắt nhanh

  • Tìm tiến trình chiếm cổng (Linux): sudo ss -tlnp | grep :80
  • Tìm tiến trình chiếm cổng (macOS): sudo lsof -i :80
  • Dừng container Docker đang xung đột: docker stop <name>
  • Dừng service hệ thống đang xung đột: sudo systemctl stop nginx
  • Dọn dẹp toàn bộ: docker compose down && docker compose up -d

Related Error Notes