
Docker "port is already allocated" 에러 해결 — 원인별 체크리스트
docker compose up 한 줄에 'port is already allocated'가 떴다면, 포트부터 바꾸지 말자. 포트를 쥔 범인을 찾는 명령, docker-proxy를 함부로 kill 하면 안 되는 이유, Windows와 macOS가 몰래 포트를 예약하는 함정까지 원인별로 정리했다.
docker compose up -d 한 줄이면 끝나야 할 아침이 있고, 빨간 글씨 한 줄에 10분이 사라지는 아침이 있다. 후자의 주인공은 거의 매번 이 문장이다.
Error response from daemon: driver failed programming external
connectivity on endpoint web: Bind for 0.0.0.0:8080 failed:
port is already allocated
이걸 처음 만나면 열에 아홉은 포트 번호부터 바꾼다. 8080을 8081로, 안 되면 8082로. 그렇게 도망치다 보면 어느 날 docker-compose.yml이 9091, 15432 같은 기억도 안 나는 숫자로 도배돼 있다. 정작 포트를 쥔 범인은 그대로 살아 있는데 말이다.

'allocated'와 'in use'는 같은 에러가 아니다
먼저 짚고 갈 게 있다. 거의 똑같이 생긴 두 메시지는 출처가 다르고, 그래서 해결법도 갈린다.
port is already allocated— 도커 엔진 자신의 장부에서 나온 말이다. "내가 이 포트를 이미 어떤 컨테이너에 배정해 뒀다"는 도커 내부 기록.bind: address already in use— 호스트 OS의 네트워크 스택이 돌려준 거부다. 도커 바깥의 어떤 프로세스가 그 포트를 먼저 물고 있다는 뜻.
전자는 대개 "죽은 줄 알았는데 안 죽은 컨테이너"가 원인이고, 후자는 "도커 밖의 프로그램, 혹은 OS 자신"이 원인이다. 범인 수색을 시작하기 전에 이 둘을 구분해 두면 엉뚱한 곳을 파는 시간을 줄인다.
1단계 — 포트를 쥔 범인부터 특정한다
포트를 바꾸는 건 그다음이다. 먼저 누가 그 포트를 잡고 있는지 본다. 도커 컨테이너가 범인인지부터 확인한다.
docker ps --filter "publish=8080"
여기서 뭔가 잡히면 답은 간단하다. 바깥 프로세스까지 뒤지려면 OS 명령으로 넘어간다.
# 리눅스 · macOS — 8080을 LISTEN 중인 프로세스
lsof -i :8080 -sTCP:LISTEN
# ss를 쓴다면
ss -ltnp | grep ':8080'
출력의 COMMAND와 PID가 범인의 신원이다. 이름이 com.docker...나 docker-proxy면 도커가, ControlCenter나 node, postgres면 호스트 프로세스가 쥐고 있는 것이다.

범인별 처방전
유령 컨테이너 / 고아 컨테이너. compose down을 안 하고 파일만 고쳤거나, 이전 세션의 컨테이너가 떠 있는 경우다. 가장 흔한 시나리오다.
docker compose down --remove-orphans
docker container prune -f
이름 모르는 컨테이너가 포트만 물고 있을 때. 포트로 직접 지목해 세운다.
docker stop $(docker ps -q --filter "publish=8080")
호스트 프로세스가 범인일 때. 앞의 lsof로 PID를 알아냈다면 그 프로세스를 끈다. 단 kill -9는 최후의 수단이고, 가능하면 그 프로그램을 정상 종료시키는 게 먼저다.
kill <PID> # 우선 정상 종료 시도
kill -9 <PID> # 그래도 안 죽을 때만
여기서 대부분이 한 번씩 밟는 지뢰가 있다.
lsof결과에docker-proxy가 보이면 그 PID를 직접kill하지 마라. 그건 호스트 프로그램이 아니라 도커가 포트 포워딩을 위해 띄운 프로세스다. 강제로 죽이면 도커 네트워킹이 꼬여서, 포트는 비었는데 컨테이너는 여전히 못 뜨는 더 괴상한 상태가 된다. 이때는 proxy가 아니라 그 포트를 소유한 컨테이너를docker stop으로 내려야 한다.
여기서부터가 대부분이 모르는 영역이다
포트를 분명히 비웠는데도 port is already allocated가 계속 난다면, 범인은 눈에 보이는 프로세스가 아니라 OS가 조용히 예약해 둔 포트 구간일 수 있다. 이 함정은 검색해도 잘 안 나와서 한참을 헤매게 만든다.
Windows. Hyper-V와 WSL2는 부팅 때 포트 범위를 통째로 예약해 간다. 그 구간에 걸린 포트는 netstat에도 점유 프로세스가 안 잡히는데 바인딩만 거부된다. 예약 구간부터 확인한다.
netsh interface ipv4 show excludedportrange protocol=tcp
netstat -aon | findstr :8080
내 포트가 예약 구간에 들어가 있으면, winnat을 한 번 재시작하면 예약이 풀린다(관리자 권한).
net stop winnat
net start winnat
macOS. 맥에서 5000번이나 7000번 포트가 "이미 쓰이는 중"이라면, 십중팔구 당신 앱이 아니라 AirPlay 수신기(ControlCenter)가 범인이다. 몬터레이 이후 macOS가 이 포트들을 기본으로 점유한다.
lsof -i :5000
# COMMAND 칸에 ControlCenter가 보이면 그게 범인
이건 강제로 죽여도 OS가 되살린다. 시스템 설정 → 일반 → AirDrop 및 Handoff에서 'AirPlay 수신기'를 꺼 버리거나, 그냥 컨테이너 포트를 5001·8085처럼 안 겹치는 번호로 옮기는 게 깔끔하다.
국내 팀 작업에서 이 에러가 유독 자주 터지는 조합이 있다. 맥(개발)과 우분투(서버)를 섞어 쓰면서, 로컬에 PostgreSQL이나 Redis를 brew로도 깔아 두고 도커로도 띄우는 경우다. 5432, 6379는 그렇게 양쪽에서 동시에 노리는 대표적 '격전지'다. 그래서 나는 로컬 개발 포트를 아예 비표준 대역으로 밀어 버리는 쪽을 선호한다. 지금 운영 중인 서비스도 기본 포트 앞에 숫자 하나를 붙여 5432는 45432로, 6379는 46379로 쓴다. 미관상 특이해 보여도, 로컬에 이미 깔린 기본 포트와 절대 안 부딪친다는 실익이 더 크다. 하나 더 — 포트를 8080:8080 대신 127.0.0.1:8080:8080으로 바인딩하면 외부 노출도 막고 충돌 표면도 줄어든다. 사소해 보여도 보안과 충돌을 같이 잡는 습관이다.
재발을 막는 네 가지 습관
- 작업을 끝낼 때
Ctrl+C로 끊지 말고docker compose down으로 내린다. 끊기만 하면 컨테이너가 포트를 쥔 채 백그라운드에 남는 일이 생긴다. - 자주 쓰는 로컬 포트는 비표준 대역(예: 4로 시작하는 대역)으로 고정해 둔다.
- 포트 번호를
.env로 빼서 팀원마다 다른 값을 쓸 수 있게 한다. - 외부에 열 필요 없는 포트는
127.0.0.1:을 붙여 루프백에만 바인딩한다.
포트를 바꿔 도망치는 건 당장은 빠르지만, 범인을 한 번 제대로 특정해 두면 다음부터는 10초면 끝난다. 당신의 docker-compose.yml에는 지금 '도망친 흔적'인 이상한 포트 번호가 몇 개나 박혀 있는가? 나만의 포트 충돌 해결 루틴이 있다면 댓글로 공유해 달라.
댓글 0
댓글을 불러오는 중…





