You run docker compose up or docker run, and instead of a running container you get this:
Error response from daemon: driver failed programming external connectivity on endpoint web:
Bind for 0.0.0.0:8080 failed: port is already allocated
The message is blunt but accurate. Something already holds host port 8080, and Docker can’t give the same port to two listeners. The fix is short once you know which side of the problem you’re on: free the port, or publish your container on a different one.
This walkthrough covers both. You’ll find out exactly what’s holding the port — another container, a system service, or a leftover process — then decide whether to stop it or remap. The commands are for Linux hosts; the same logic applies on Docker Desktop, just with the host’s own tools.
What the error is telling you
When you write -p 8080:80, the left number (8080) is the host port and the right number (80) is the port inside the container. Docker asks the operating system to bind host port 8080 and forward it into the container. If anything already listens on 8080, the bind fails and you get “port is already allocated.”
So the question is never “why is Docker broken.” It’s “what already owns this port.” Three usual suspects:
- Another Docker container you started earlier (or one that crashed and is still around).
- A non-Docker service on the host — a system Nginx, Apache, a dev server, a database.
- A second
docker compose upfrom a different project mapping the same host port.
Step 1: Check for another container first
Most of the time the culprit is another container, so start there. List running containers and read the PORTS column:
docker ps
Look for anything showing your host port, like 0.0.0.0:8080->80/tcp. To filter quickly:
docker ps --filter "publish=8080"
Don’t stop at running containers. A container that’s stopped but not removed can still hold a published port mapping in Docker’s records, which is enough to block a new one. Show everything, including stopped:
docker ps -a
If you find a stale container you no longer need, remove it:
# Stop it if it's running, then remove
docker stop <container-name-or-id>
docker rm <container-name-or-id>
If it’s a Compose project you’re done with, take the whole stack down from its folder:
docker compose down
Step 2: Find a non-Docker process holding the port
If docker ps comes up empty, the port belongs to something on the host. Two tools tell you what, and either works.
ss ships with modern Linux (part of iproute2) and is the quickest:
sudo ss -ltnp 'sport = :8080'
That reads as: listening (-l), TCP (-t), numeric ports (-n), with the process (-p), filtered to source port 8080. The output names the process and its PID, for example users:(("nginx",pid=812,fd=6)).
lsof gives the same answer in a different shape:
sudo lsof -i :8080
On an older box without ss, fall back to netstat:
sudo netstat -ltnp | grep ':8080'
Tools for finding what owns a port
| docker ps | First check — is another container publishing this host port? |
|---|---|
| sudo ss -ltnp | Fast, modern. Lists listening sockets with process and PID. |
| sudo lsof -i :PORT | Clear per-port view; shows process, PID, and user. |
| sudo netstat -ltnp | Legacy fallback when ss isn't installed. |
Step 3: Decide — free the port or remap
Now you know what’s on the port. Pick the fix that matches the situation instead of reflexively killing things.
If it’s a stale container or a process you don’t need, stop it. For a system service, that means the service manager, not kill:
# Example: a host Nginx grabbing port 80/8080
sudo systemctl stop nginx
# If it should never start on boot again
sudo systemctl disable nginx
If it’s a real service you depend on, leave it alone and move your container to a free host port. This is almost always the right call when the conflict is with something legitimate. Change only the host side of the mapping:
# Was -p 8080:80, now publish on 8081 instead
docker run -d --name web -p 8081:80 nginx
In Compose, edit the ports: entry and re-run up:
services:
web:
image: nginx
ports:
- "8081:80" # host 8081 -> container 80
docker compose up -d
Note that only the left number changed. The container still listens on 80 internally; you’ve just picked a different door on the host.
Bind to 127.0.0.1 to avoid conflicts (and exposure)
By default -p 8080:80 binds to 0.0.0.0, every interface on the host. That can collide with other services and, worse, exposes the container to your whole network. For anything that only needs to be reached from the host itself — a database, an admin panel, a backend an app talks to locally — bind it to localhost:
docker run -d --name db -p 127.0.0.1:5432:5432 postgres:16
Or in Compose:
services:
db:
image: postgres:16
ports:
- "127.0.0.1:5432:5432"
This does two useful things. It sidesteps conflicts with whatever might be bound on the public interface, and it keeps the service off the LAN entirely. A Postgres or Redis container with no business being reachable from other machines shouldn’t be published on 0.0.0.0 in the first place.
Quick checklist
Clearing a 'port is already allocated' error
- Run docker ps and docker ps -a — is another container (running or stopped) on that host port?
- If yes and you don't need it: docker stop + docker rm, or docker compose down
- If no container: sudo ss -ltnp 'sport = :PORT' or sudo lsof -i :PORT to find the process
- Real service you need? Remap your container to a free host port instead
- Service only used locally? Bind it to 127.0.0.1 to avoid conflicts and exposure
- Re-run docker run / docker compose up -d and confirm it starts
Why the port can stay busy after you stop a container
A frequent follow-up: “I stopped my container and the port is still taken.” A few reasons this happens.
The container stopped but wasn’t removed, and Docker still tracks its mapping — docker ps -a will show it. Remove it with docker rm. Or a previous run crashed and left a container in a half-dead state; same fix, docker rm it and start fresh. Sometimes it’s genuinely a different container that grabbed the mapping in the meantime, which docker ps will reveal.
If a stuck container won’t remove cleanly, force it:
docker rm -f <container-name-or-id>
If the daemon itself seems confused after a crash, restarting it clears its connectivity state:
sudo systemctl restart docker
That’s a heavier hammer — it restarts every container without a restart policy too — so use it when individual docker rm hasn’t sorted things out.
Wrapping up
“Port is already allocated” is one of Docker’s friendlier errors because it points straight at the cause: a host port that’s already taken. Work it in order. Check docker ps and docker ps -a for a container holding the port, then ss or lsof for a host process. From there it’s a clean decision — stop what you don’t need, remap onto a free host port what you do, and bind local-only services to 127.0.0.1.
If you hit this while bringing up a multi-container app, the Docker Compose beginner guide shows how the ports: block works, and the rest of the Docker & Containers guides cover more day-to-day fixes.