Quick fix
# One-liner: first check whether a docker container holds it, then host processes, finally switch ports
docker ps | grep <port> ; ss -tlnp | grep <port>
What you will run into
docker run -p <host-port>:<container-port> or docker compose up fails to create the container with failed to bind host port 0.0.0.0:8080/tcp: address already in use or Bind for :::8090 failed: port is already allocated.
The same -p command used to work, then one day it suddenly errors — the host port is occupied.
Why this happens
-p host-port:container-port creates a port-forward listener on the host (docker-proxy listens on 0.0.0.0:host-port). A host port can be held by only one process/container at a time. Two main sources of occupation:
| Source | Examples |
|---|---|
| Other host processes | python http.server, nginx, VSCode port forwarding |
| Other docker containers | Another container mapped to the same host port (two compose services colliding, stale containers not cleaned up) |
-p hostPort:containerPort creates a forwarding listener on the host (docker-proxy listening on 0.0.0.0:hostPort), mapping "host port → container port".
A host port is a global host resource that can have exactly one listener at a time — whether the occupant is a host process or another container's forward rule.
Deterministic takeaway: on port is already allocated, run ss -lntp | grep <port> to see who is listening;
if it is docker-proxy, another container owns the port — look with docker ps -a, because exited containers keep their port mappings, and "not visible in docker ps" does not mean the port is free;
if it is a host process, change the port or stop the process.
Step by step
1. First check whether a docker container holds it — Required
docker ps
Look for the port in the Ports column. If present, docker rm -f <old-container> (or stop + rm) — but first confirm no other service needs it.
2. Then check host processes — Required
ss -tlnp | grep <port>
(Or lsof -i:<port> / netstat -tulpn | grep <port>.) The PID/process name tells you who holds it — stop it if it's a temporary process, or switch ports if it's a long-running service.
3. Switch the host port (easiest) — Required
docker run -p 8081:80 <image>
In compose change the left side of ports: (e.g. "8081:80"), or manage it via an env var (${LOBE_PORT}:3210 style).
4. Check for conflicts inside compose — Diagnose
Two services in the same compose file mapping the same host port fight each other; check that the left-hand values in ports: are unique.
How to confirm it works
Works after switching ports (measured output shape):
docker run -p 8081:80 → success
docker port <container> → 80/tcp -> 0.0.0.0:8081Other environments
| Environment | Notes |
|---|---|
| Host process holds it | Locate PID with ss -tlnp, stop the process or switch ports (measured chain A) |
| Container holds it | Locate via docker ps Ports column, rm -f the old container (measured chain B) |
| Inside compose | Left-hand values of ports must be unique |
| IPv6 side | Bind for :::8090 failed = container-occupation form (measured) |
Symptom
docker run -p <host-port>:<container-port> or docker compose up fails to create the container, in two typical forms:
# Form 1: another process on the host holds the port
docker: Error response from daemon: failed to set up container networking:
driver failed programming external connectivity on endpoint <name>:
failed to bind host port 0.0.0.0:8080/tcp: address already in use
# Form 2: another docker container holds the port (often reported on the IPv6 side)
docker: Error response from daemon: failed to set up container networking:
driver failed programming external connectivity on endpoint <name>:
Bind for :::8090 failed: port is already allocated
Key traits: the error always contains port is already allocated or address already in use and names the host port (0.0.0.0:PORT / :::PORT); the same -p command worked before and broke one day; or it fails right away when following a tutorial/another compose file (port collision).
Cause
-p host-port:container-port creates a port-forward listener on the host (docker-proxy listens on 0.0.0.0:host-port). A host port can only be held by one process/container at a time. Two main sources:
- Another process on the host: you, the system, or another service (python http.server, nginx, editor port forwarding) holds it — most common;
- Another docker container: another container's
-pmaps the same host port (including two services in one compose file, or a leftover old container).
Solution
- Check docker containers first:
docker ps(look at the Ports column for the port). If found,docker rm -f <old-container>(ordocker stop+docker rm) — make sure nothing else needs it first. - Then check host processes:
ss -tlnp | grep <port>(orlsof -i:<port>/netstat -tulpn | grep <port>) — the PID/process name tells you who holds it; stop temporary processes, or change the port for long-running services. - Change the host port (easiest):
-p 8081:80to an unused port; in compose edit the left side ofports:(e.g."8081:80") or manage ports via environment variables (${LOBE_PORT}:3210). - Check for in-compose collisions: two services in the same compose file mapping the same host port fight each other — make the left-side values unique.
Verification Evidence
- Local repro (2026-08-29, WSL Ubuntu, docker 29.1.3, alpine:3.20):
- Chain A (host process): python3 http.server on 8080 →
docker run -p 8080:80→failed to bind host port 0.0.0.0:8080/tcp: address already in use(exit 125);ss -tlnppinpointed the python3 process (PID visible); after killing it,-p 8081:80succeeded —docker portshows80/tcp -> 0.0.0.0:8081, docker-proxy listening on 0.0.0.0:8081 and [::]:8081; - Chain B (container): container 1
-p 8090:80OK → container 2 taking the same port →Bind for :::8090 failed: port is already allocated(exit 125);docker psPorts column identified the old container; afterdocker rm -f, the same port worked again.
Evidence details kept locally (2026-08-29)
Related canonicals
Read in order: precondition -> same root / same domain / related:
- Precondition (rule out first):
- Cannot connect to the Docker daemon (precondition: confirm the daemon is alive first; port errors only make sense then)
Applicable tools (1)
This problem is linked from the tool pages below — reachable from either side.
dockerBundled solutions (combos)
This problem is part of the following verified bundles.
Source & evidence
← Coding Agents & Terminal domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk