Skip to content
DevOps AI ToolKit
Newsletter
All guides
Docker with AI By James Joyner IV · · 9 min read

Docker Error Guide: 'port is already allocated' Container Port Bind Failures

Fix Docker 'port is already allocated' and 'address already in use' bind errors: free conflicting containers, host processes, stale docker-proxy, and remap ports.

  • #docker
  • #troubleshooting
  • #errors
  • #networking

Exact Error Message

When you docker run -p 8080:80 ... or docker compose up and something already owns the host port, the daemon refuses to start the container and prints:

docker: Error response from daemon: driver failed programming external connectivity on endpoint web (a1b2c3...): Bind for 0.0.0.0:8080 failed: port is already allocated.

A closely related variant comes straight from the kernel when the host socket is taken by a non-Docker process:

Error response from daemon: driver failed programming external connectivity on endpoint web: failed to bind host port for 0.0.0.0:8080:172.17.0.2:80/tcp: address already in use

The container is created but never transitions to a running state, so a follow-up docker ps -a shows it as Created or Exited.

What It Means

Docker publishes a container port to the host by binding a socket on the host’s network namespace — either through docker-proxy (the userland proxy) or directly via an iptables/nftables DNAT rule. To bind 0.0.0.0:8080, the host must have that (address, port, protocol) tuple free. port is already allocated is Docker’s own bookkeeping saying it already handed 8080/tcp to another container endpoint. address already in use (EADDRINUSE) is the kernel saying a socket — Docker’s or anyone else’s — already holds it.

The distinction matters: the first usually means another container (or a stale docker-proxy) owns the port; the second often means a plain host process (nginx, a dev server, systemd socket) is listening. Either way the new container cannot publish to that host port until the holder is released or you map to a different one.

It is worth being precise about what “the port” means here. A binding is a tuple of address, port, and protocol — 0.0.0.0:8080/tcp is a different reservation from 0.0.0.0:8080/udp, and 127.0.0.1:8080 does not conflict with 192.168.1.10:8080. When you write -p 8080:80, Docker expands the host side to 0.0.0.0:8080/tcp, the broadest possible binding, which is why two such mappings always collide even if you intended them for different interfaces. Pinning the host side explicitly (-p 127.0.0.1:8080:80) narrows the reservation and can let two containers coexist on the same numeric port across different addresses. The conflict is always evaluated on the host network namespace, never inside the container, so the container-side port (the :80 half) is irrelevant to this error.

Common Causes

  • Another running container already publishes the port. Two -p 8080:... mappings on the same host port can never coexist.
  • A stale/exited container still holds the reservation. A container that crashed without clean teardown can leave the allocation registered.
  • A lingering docker-proxy process. After an unclean daemon stop, a docker-proxy for the old container can keep the host socket bound.
  • Restart race. restart: always or a fast docker compose down && up can race a still-shutting-down container that hasn’t freed the port.
  • Duplicate -p flags. The same host port mapped twice in one run or two services in a compose file colliding.
  • A host service on the port. nginx, Apache, a systemd-managed daemon, or a local dev server already listening on 8080.

The first three causes are Docker’s own state being out of sync, and the fix is usually to clear a container or restart the daemon. The last three are external — another mapping, a race, or a non-Docker listener — and require either changing your mapping or stopping the other holder. The diagnostic step below tells you which family you are in: if docker ps shows a container on the port, it is Docker’s; if only ss/lsof shows it, it is a host process or an orphaned docker-proxy.

How to Reproduce the Error

Start one container that publishes 8080, then start a second on the same host port:

docker run -d --name web1 -p 8080:80 nginx:alpine
docker run -d --name web2 -p 8080:80 nginx:alpine

The second command fails immediately with Bind for 0.0.0.0:8080 failed: port is already allocated. To reproduce the kernel variant, start a host listener first, then try to publish over it:

python3 -m http.server 8080 &
docker run -d -p 8080:80 nginx:alpine

This yields address already in use, because a non-Docker process owns the socket.

Diagnostic Commands

Find which container (if any) already publishes the port:

docker ps --format 'table {{.Names}}\t{{.Ports}}' | grep 8080
docker port web1

If no container claims it, look at the host’s listening sockets — this reveals both docker-proxy and plain processes:

sudo ss -ltnp 'sport = :8080'
sudo lsof -i :8080
LISTEN 0  4096  0.0.0.0:8080  0.0.0.0:*  users:(("docker-proxy",pid=4412,fd=4))

A docker-proxy with no matching running container is a stale process. Inspect Docker’s own view and recent lifecycle events:

docker network inspect bridge | grep -A3 -i ports
docker events --since 10m --filter event=start --filter event=die

If you suspect the daemon’s internal allocation table is out of sync, check the journal:

sudo journalctl -u docker --since "15 min ago" | grep -i 'port\|bind\|allocated'

Step-by-Step Resolution

  1. Identify the holder. Run docker ps filtered on the port; if a container owns it, decide whether to stop it or remap the new one.

    docker stop web1   # frees 8080 if web1 legitimately should yield it
  2. Clear a stale/exited container. Remove the dead container that still holds the reservation:

    docker rm -f $(docker ps -aq --filter status=exited)
  3. Kill a lingering docker-proxy. If ss/lsof shows a docker-proxy with no live container, it is orphaned. Restarting the daemon clears these safely:

    sudo systemctl restart docker
  4. Stop a conflicting host service. If the holder is nginx/systemd rather than Docker, stop or reconfigure it:

    sudo systemctl stop nginx
  5. Remap to a free host port if the holder must keep running. Change only the host side of the mapping:

    docker run -d -p 8081:80 nginx:alpine
  6. Fix duplicate compose mappings. Ensure no two services share a host port, and avoid mapping the same host port twice in one run.

After the holder is released or the mapping changed, re-run the container and confirm with docker ps that it reaches Up.

A note on the restart race specifically: if a container with restart: always keeps grabbing the port milliseconds before your new one, the holder will not appear in a single docker ps snapshot reliably. Watch the lifecycle with docker events while you redeploy, or stop the restart-looping container explicitly (docker update --restart=no <name> && docker stop <name>) before bringing up the replacement, so the reservation is held by exactly one container at a time.

How to Prevent the Issue

  • Reserve a distinct host port per service and document the allocation; treat host ports as a shared, finite resource.
  • Prefer letting Docker assign an ephemeral host port (-p 80 or -p ::80) for throwaway containers, then read the chosen port with docker port.
  • Use docker compose down (not just stop) before up to guarantee the previous network and port reservations are torn down before the new ones bind.
  • Avoid binding services on the host to the same ports you publish from containers; pick a clear split (e.g., host apps on 80/443, containers on 8000+).
  • After daemon crashes, run sudo systemctl restart docker to clear orphaned docker-proxy processes before redeploying.
Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.