What this error means
Docker tried to publish a container port on your host (0.0.0.0:PORT), but another process or container is already listening there. You must either free the port or choose a different mapping.
Common message:
- Ports are not available: listen tcp 0.0.0.0:PORT: bind: address already in use
Applies to Linux, macOS, and Windows/WSL2 (Docker Desktop).
Quickstart (most cases)
- Identify who is using the port
- Linux/macOS:
- sudo lsof -nP -iTCP:8080 -sTCP:LISTEN
- or: sudo ss -ltnp | grep :8080
- Windows PowerShell (Admin):
- Get-NetTCPConnection -LocalPort 8080 -State Listen | Select OwningProcess,LocalAddress,LocalPort
- Get-Process -Id <PID>
- Stop the listener
- Linux/macOS: sudo kill -9 <PID> or sudo fuser -k 8080/tcp
- Windows: Stop-Process -Id <PID> -Force
- If it’s a service (e.g., nginx, IIS), stop/disable the service.
- Retry your container
- docker compose up -d
If the port must remain in use, change your host port mapping instead (for example, map 8081:80).
Minimal Working Example
Create a Compose file that publishes a container port.
docker-compose.yml:
version: "3.8"
services:
web:
image: nginx:stable-alpine
ports:
- "8080:80" # host:container
Run:
docker compose up -d
If something is already listening on 8080, you will see the bind error. Free 8080 or change the mapping to a free port, e.g. "8081:80", then rerun.
Step-by-step diagnosis and fix
- Check if another container already publishes the port
- List containers publishing a specific port:
docker ps --filter "publish=8080"
- Stop/remove the conflicting container:
docker stop <container>
docker rm <container>
- Find host processes using the port
- Linux/macOS:
sudo lsof -nP -iTCP:8080 -sTCP:LISTEN
# or
sudo ss -ltnp | grep :8080
- Windows (PowerShell as Administrator):
Get-NetTCPConnection -LocalPort 8080 -State Listen | Select-Object OwningProcess,LocalAddress,LocalPort
Get-Process -Id <PID>
- Free the port
- Linux/macOS:
sudo fuser -k 8080/tcp # kill by port
# or
sudo kill -9 <PID>
- Windows:
Stop-Process -Id <PID> -Force
If the process restarts automatically, disable or stop the service instead (systemd, launchd, Windows Services).
- Change your mapping if you cannot free the port
- docker run examples:
# Bind only to loopback
docker run -d -p 127.0.0.1:8081:80 nginx:stable-alpine
# Use a random host port
docker run -d -p :80 nginx:stable-alpine # host port chosen automatically
- Compose examples (ports section):
# Bind to a specific host IP and port
ports:
- "127.0.0.1:8081:80"
# Let Docker choose a random host port
ports:
- "127.0.0.1::80"
- Verify
docker compose ps
curl -I http://127.0.0.1:8081 # adjust to your mapping
Windows/WSL2 and Docker Desktop specifics
- Check Windows services that commonly occupy ports (e.g., IIS uses 80/443, SQL Server, Web Deployment Service, etc.). Stop them if not needed.
- Reserved/excluded port ranges can block binds even if no process is visible:
netsh interface ipv4 show excludedportrange protocol=tcp
Pick a port outside excluded ranges. Avoid ephemeral dynamic ranges too:
netsh interface ipv4 show dynamicport tcp
- If running Docker Desktop with WSL2 backend, a Windows process (com.docker.backend) forwards ports. Conflicts still follow the same rules: free the port on Windows, not just inside WSL.
- If a security product or VPN binds to many ports, prefer binding to 127.0.0.1 instead of 0.0.0.0, or choose a different host port.
Alternatives and best practices
- Bind to localhost when external access is unnecessary:
ports:
- "127.0.0.1:8080:80"
- Use a reverse proxy to reduce the number of published ports. Publish only the proxy’s ports; connect internal services via a Docker network.
- Prefer dynamic host ports for local development to avoid conflicts:
ports:
- "127.0.0.1::80" # random host port for container port 80
- For Linux-only performance and fewer moving parts, consider host networking (not available on Docker Desktop for Mac/Windows):
docker run --network host nginx:stable-alpine
Pitfalls
- Killing the wrong PID: Always confirm the owning process before stopping it.
- Auto-restarting services: systemd, launchd, or Windows Services may respawn listeners. Disable or stop the service properly.
- Multiple Compose projects: The same port can be used by a different project. Use docker ps --filter "publish=PORT" to locate it.
- Binding to 0.0.0.0 unnecessarily exposes services externally. Use 127.0.0.1 for local-only development.
- Windows excluded ports: Even with no visible listener, a port in an excluded range won’t bind. Choose a different port.
Performance notes
- On macOS/Windows (Docker Desktop), published ports are forwarded through a user-space networking layer. Publishing many ports can add overhead. Consolidate behind a single reverse proxy port when possible.
- Binding to 127.0.0.1 avoids external firewall paths and can be slightly cheaper than 0.0.0.0 for local dev.
- On Linux, iptables-based NAT for -p is efficient, but if you need maximum throughput and lowest latency, use --network host (security implications apply) or keep the number of published ports minimal.
Tiny FAQ
Q: Can Docker choose a random host port?
- A: Yes. Use -p :CONTAINERPORT with docker run or "HOSTIP::CONTAINERPORT" in Compose. Docker will select a free host port.
Q: I freed the port, but I still get the error. Why?
- A: Another process may have grabbed it in the meantime, a service restarted, or the port is in an excluded range on Windows. Re-run the checks and consider using a different host port.
Q: How do I see which container owns a port?
- A: docker ps --filter "publish=PORT" or docker inspect <container> | grep -i "HostPort".
Q: Is binding to 0.0.0.0 required?
- A: No. Use 127.0.0.1 if you only need local access. Bind to a specific interface/IP when exposing externally.
Q: Does EXPOSE in Dockerfile cause conflicts?
- A: No. EXPOSE is metadata. Conflicts occur only when you publish ports with -p/--publish or Compose ports.