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 composekhá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

