Docker · beginner

Docker bind address already in use: trace the port owner

Docker's "bind: address already in use" appears when a published container port cannot bind because another process already owns that host socket. Trace the conflicting listener, decide whether to change the host port or stop the owner, then verify with a fresh container run.

The symptoms

  • docker run exits immediately with a message containing "bind: address already in use" or "Error response from daemon: driver failed programming external connectivity".
  • docker compose up fails on a service with ports mapping, while other services on the same compose file start successfully.
  • A previously working container now fails to start after a host reboot, an IDE restart, or another container was launched with the same host port.
  • Stopping and restarting the same container succeeds sometimes and fails other times, suggesting another process is intermittently holding the port.

Likely causes

  • Another container is already publishing the same host port with the same or a conflicting protocol (TCP vs UDP).
  • A non-Docker host process such as a web server, database, IDE preview server, or system service is bound to the requested host port.
  • A previous container instance is in an unexpected state and still holds the port mapping through the Docker proxy.
  • The host firewall or socket configuration restricts the requested bind address, so the bind collides with a restricted listener.
  • Port collision inside a shared compose project where two services map the same host port by accident.

First ten minutes

  1. 01Capture the exact error line and the service name from docker compose ps or docker ps output so the failing port mapping is unambiguous.
  2. 02Identify which host port is in conflict by reading the ports field of the failing service and the bind address if one is specified.
  3. 03List every container that publishes any port so you can spot duplicate host bindings across the local Docker daemon.
  4. 04Ask the host operating system which process owns the conflicting socket, narrowing between Docker and a non-Docker process.
  5. 05Decide the next move: change the host port for the new container, or stop the existing owner, based on which side is expendable.

Evidence to collect

  • The full daemon error line including the bind address, port number, and protocol from the failing docker run or docker compose up invocation.
  • Output of docker ps -a showing every container, its status, and its published host ports, so duplicates can be spotted.
  • Host-level listener inventory for the conflicting port and protocol, identifying the process name and PID.
  • The compose file ports entries for the failing service, including any explicit bind address and protocol suffix.
  • Recent changes: recent reboots, new services, or new containers that could have introduced the new listener.

Where to look

  • Docker daemon boundary: the daemon translates a container port mapping into a host socket through its built-in proxy, so collisions appear at the host network boundary, not inside the container network namespace.
  • Host network namespace: processes outside Docker that bind directly to the host IP stack, including system services and user applications.
  • Compose project boundary: when several services share one compose file, duplicates inside that file are the most common source of intermittent collisions.
  • Docker proxy and iptables rules on Linux, where Docker inserts forwarding rules for published ports, so a stale rule can preserve a bind after the owning container stops.

Diagnostic steps

  1. 01Read the exact error from the failed invocation and extract the port number and protocol; confirm it matches the service's declared ports mapping in the compose file.
  2. 02Run docker ps -a to enumerate containers and their published host ports, then mark every container that publishes the suspected host port.
  3. 03Run docker compose ps for compose-managed projects to see which service failed and which succeeded, isolating the collision to a single mapping.
  4. 04Run ss -ltnp 'sport = :<port>' for TCP or ss -lunp 'sport = :<port>' for UDP to identify the process bound to the host port and capture its PID and command.
  5. 05Compare the owner process to docker-proxy and to known system services to classify the collision as Docker-internal versus host-external.
  6. 06If the owner is docker-proxy, identify the source container with docker ps using the listening PID and stop that container before retrying the new mapping.
  7. 07If the owner is not docker-proxy, decide between freeing the host port and reassigning the new container to a different host port without touching the existing owner.

Common mistakes

  • Restarting Docker as a first reflex, which can take down unrelated workloads and does not prove whether a non-Docker process owns the port.
  • Changing the container port instead of the host port, which alters the service contract exposed to other containers and clients.
  • Assuming "it worked yesterday" rules out a collision, when in practice host services, IDE previews, or prior containers frequently rebind the same port after a reboot.
  • Killing the owning process without recording its PID and command, leaving no evidence for a postmortem and risking an unrelated service being terminated.
  • Editing the compose file's container port when the actual conflict is on the host side, since the published host port is the one that must change or be freed.

Safe fixes

  • If another container owns the host port, stop only that verified disposable container after confirming its identity with docker ps, then retry the failing docker run or docker compose up.
  • If a host process owns the port, keep that process running and change the published host port for the new container in the compose file or docker run -p argument to a free host port, then update any client configuration that depends on the old host port.
  • If two services inside the same compose file publish the same host port by accident, assign distinct host ports and document the chosen mapping so future runs do not collide.
  • If the bind address is explicitly set and conflicts, either remove the explicit bind address to let Docker choose a valid host binding or align it with an address that is free for that port.
  • If protocol matters, verify the existing owner is on the same protocol before declaring a collision, since TCP and UDP bindings on the same port number are independent.

Prove the fix

  1. 01The failing docker run or docker compose up command exits with a normal started status and no bind error, and the container reaches a running state within a short timeout.
  2. 02docker ps shows exactly one container publishing the chosen host port and protocol, confirming no duplicate bind remains.
  3. 03ss -ltnp 'sport = :<port>' or its UDP equivalent lists docker-proxy as the owner of that host port, with the expected container PID, proving Docker now controls the binding.
  4. 04A second restart of the same container reproduces the success, demonstrating that the fix is not a one-off race and that the chosen host port remains free.

Prevention and next steps

  • Assign host ports through a documented range per project and avoid hard-coding the same host port in multiple compose files that may run on the same host.
  • Audit docker ps output before starting a new container that publishes a well-known port, so duplicate publishes are detected before they cause a bind failure.
  • Prefer compose project isolation or distinct host ports for services that share the same internal container port, so accidental collisions are avoided at design time.
  • Document which host services own commonly used ports on developer workstations so new containers can be assigned ports that are demonstrably free.

Safe commands and checks

docker ps -a --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'
docker compose ps
ss -ltnp 'sport = :<port>'
ss -lunp 'sport = :<port>'
If the listener belongs to a disposable test container, stop only that verified container after confirming its name, owner, and dependencies; do not stop an unknown or shared workload.
Remove only the verified disposable container after confirming it is stopped and its data is not needed; preserve the container when ownership is uncertain.