Docker & Containers

Docker Compose "Port Already in Use": How to Fix

The Bind for 0.0.0.0:3000 failed: port is already allocated error means something else is already listening on that port. Here's how to find it and fix it.

Quick Fix (3 steps)
  1. 1. Find the process: lsof -i :3000 (Mac/Linux) or netstat -aon | findstr :3000 (Windows)
  2. 2. Stop it: kill PID — or change your Compose host port
  3. 3. Run docker compose up again

What the Error Looks Like

Docker Compose surfaces two common port error messages:

Error response from daemon: driver failed programming external connectivity
on endpoint myapp-web-1 (abc123...):
Bind for 0.0.0.0:3000 failed: port is already allocated
Error starting userland proxy: listen tcp4 0.0.0.0:5432:
bind: address already in use

Both mean the same thing: the port you're trying to expose on the host is already bound to another process. Docker cannot claim a port that's already in use.

Step 1: Find What's Using the Port

On Mac / Linux

# Replace 3000 with your port
lsof -i :3000

Output looks like:

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 4521 alice 22u IPv4 ... 0t0 TCP *:3000 (LISTEN)

The PID column (4521 in this example) is the process ID. The COMMAND column shows what's running. You can also use ss -tlnp | grep :3000 on Linux.

On Windows

# In Command Prompt or PowerShell
netstat -aon | findstr :3000

Output looks like:

TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 4521

The last column is the PID. To find the process name:

tasklist | findstr 4521

Check for other Docker containers

# List all running containers and their ports
docker ps
# Or show all containers including stopped ones
docker ps -a

A previous container might still be running in the background. Stop it withdocker stop CONTAINER_IDbefore running your compose command again.

Step 2: Three Ways to Fix It

Option A: Stop the conflicting process

# Mac / Linux — replace 4521 with your PID
kill 4521
# If it doesn't stop, use force kill
kill -9 4521
# Windows — replace 4521 with your PID
taskkill /PID 4521 /F

Best when the conflicting process is a dev server you forgot was running. After stopping it, docker compose up should work.

Option B: Change the host port in docker-compose.yml

The port mapping format is HOST_PORT:CONTAINER_PORT. You only need to change the host side:

Before (port 3000 in use)
services:
web:
ports:
- "3000:3000"
After (use 3001 on host)
services:
web:
ports:
- "3001:3000"

Your app inside the container still runs on port 3000. It's just exposed on port 3001 on your machine. Access it at http://localhost:3001.

Option C: Use environment variables for flexible port mapping

For teams where different developers may have different ports in use:

# docker-compose.yml
services:
web:
ports:
- ${HOST_PORT:-3000}:3000
# .env file (or export in shell)
HOST_PORT=3001

The {:-3000} syntax means "use HOST_PORT if set, otherwise default to 3000".

Special Cases

"Nothing is using that port" but Docker still fails

On Windows, the Port Proxy service (portproxy/hyper-v) can reserve port ranges. Check: netsh interface portproxy show all. Also check: netsh int ipv4 show excludedportrange protocol=tcp. If your port is in the excluded range, Docker can't use it. Use a different port.

Port in use immediately after killing the process

The socket may be in TIME_WAIT state. This is normal TCP behavior — the OS holds the port for ~60 seconds after close. Either wait 60 seconds or use a different port temporarily.

postgres:5432 or redis:6379 already in use

You likely have a local installation of PostgreSQL or Redis running as a system service. On Mac: brew services stop postgresql@16. On Linux: sudo systemctl stop postgresql. Alternatively, map to a different host port: "5433:5432".

docker compose down but port still in use

docker compose down stops and removes containers but if a container exited unexpectedly its port mapping may persist temporarily. Try: docker rm -f $(docker ps -aq) to force-remove all containers, then verify with docker ps -a.

Frequently Asked Questions

Why does Docker say a port is already in use when nothing is running?

Several causes: (1) A Docker container is running in the background — check with docker ps. (2) On Windows, Hyper-V or WSL2 reserves port ranges — check with netsh int ipv4 show excludedportrange protocol=tcp. (3) A process exited but its socket is still in TIME_WAIT state — wait 60 seconds. (4) A system service (PostgreSQL, Redis) is running — stop it or remap the port.

How do I prevent port conflicts in a team environment?

Use environment variables in docker-compose.yml: ports: ["${HOST_PORT:-3000}:3000"]. Each developer sets their HOST_PORT in a local .env file (which is gitignored). This way different developers can use different host ports without modifying the shared docker-compose.yml.

What is the difference between the host port and container port?

In Docker's port mapping "HOST:CONTAINER", the HOST port is what you use on your machine (localhost:HOST), and the CONTAINER port is what the application listens on inside the container. You can use any available HOST port to expose any CONTAINER port. For example, "8080:3000" means your app inside runs on 3000, but you access it via localhost:8080.

docker compose down vs docker compose stop — which frees the port?

Both stop containers and free ports. docker compose stop just stops the containers (they remain, can be restarted with docker compose start). docker compose down stops AND removes the containers. For port conflicts, either works. If ports remain allocated after stop, use docker compose down to fully remove containers.

Key Takeaways

  • Port conflicts mean another process is already listening on that host port.
  • Find the process with lsof -i :PORT (Mac/Linux) or netstat -aon | findstr :PORT (Windows).
  • Fix options: stop the conflicting process, change the host port in docker-compose.yml, or use env variables for flexible mapping.
  • Common culprits: a forgotten dev server, a system PostgreSQL/Redis service, or a zombie Docker container.
  • On Windows, check Hyper-V excluded port ranges with netsh int ipv4 show excludedportrange protocol=tcp.

Related Resources