Docker

docker: port is already allocated

Something already holds that host port, often a container you forgot, sometimes a host process. How to find the owner in one command and stop it happening again.

easy fix6 min read3 causes

docker. The error
docker: Error response from daemon: driver failed programming external
connectivity on endpoint api (a3f2c1d4): Bind for 0.0.0.0:3000 failed:
port is already allocated.

Error response from daemon: Ports are not available: exposing port
TCP 0.0.0.0:5432 -> 0.0.0.0:0: listen tcp4 0.0.0.0:5432: bind: address already in use

Do this first3 steps

Run these in order. Each one tells you what its output means before you change anything.

  1. 1

    Find out whether a container holds the port

    docker ps -a --filter "publish=3000" --format '{{.Names}}\t{{.Status}}'

    Anything listed here owns the binding, running or exited. docker stop releases it; an exited container needs docker rm before the port comes back.

  2. 2

    If Docker shows nothing, it is a host process

    sudo lsof -i :3000

    Usually a local Postgres, another dev server or a system service. Stop that process, or publish on a different host port with -p 3001:3000.

  3. 3

    If you use Compose, check for another stack still up

    docker compose ls

    Another project left its stack running. Change to that directory and run docker compose down. Stopping a single container leaves the rest of the stack and its network behind.

All 8 sections

The host port you asked Docker to publish is already bound by something. Docker cannot share a port, so the container refuses to start.

The owner is one of three things: another container, a stopped container Docker still has a reservation for, or an ordinary process on the host. Identifying which takes one command each.

Find the owner

Start with containers, since that is the usual answer:

docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' | grep 3000
api-old    Exited (0) 2 days ago    
api        Up 4 minutes             0.0.0.0:3000->3000/tcp

If nothing there holds it, check the host:

sudo lsof -i :3000
COMMAND     PID   USER   FD   TYPE  DEVICE NODE NAME
node      48213  alice   22u  IPv4 0x8f3c1  TCP *:3000 (LISTEN)
# Linux alternative
sudo ss -tlnp | grep :3000
LISTEN 0 511 0.0.0.0:3000 0.0.0.0:* users:(("node",pid=48213,fd=22))

A node, python or java process is a development server someone started outside Docker. docker-proxy means Docker itself still holds it. See below.

Cause 1: Another container

docker ps --filter "publish=3000" --format '{{.Names}}'
docker stop api-old

Compose users: a project left running from another directory is the classic case, because Compose namespaces containers by project but not host ports.

docker compose ls
NAME             STATUS       CONFIG FILES
orders-api       running(3)   /Users/alice/work/orders/compose.yaml
orders-api-old   running(2)   /Users/alice/work/orders-old/compose.yaml
cd ~/work/orders-old && docker compose down

Cause 2: A stopped container still holding the reservation

The confusing one. A container that exited can leave Docker's port mapping in place, so lsof shows docker-proxy with no running container to explain it:

sudo lsof -i :3000
COMMAND       PID  USER   FD   TYPE  NODE NAME
docker-pr   12043  root    4u  IPv6  TCP *:3000 (LISTEN)
docker ps -a --filter "status=exited" --format '{{.Names}}\t{{.Ports}}'
docker rm api-old

docker stop is not enough here. The container must be removed. If removing it does not release the port, the daemon's networking state is stale:

sudo systemctl restart docker

On Docker Desktop, restart the application. That is a blunt instrument and it does resolve the case where nothing visible owns the port.

Cause 3: A host process

sudo lsof -i :5432
COMMAND    PID     USER   FD   TYPE NODE NAME
postgres   894 postgres    7u  IPv6  TCP *:5432 (LISTEN)

A locally installed PostgreSQL, MySQL, Redis or nginx conflicting with the containerised one. This is extremely common on developer machines, where 5432, 3306, 6379 and 80 are frequently taken by services installed years earlier.

Two options. Stop the host service:

sudo systemctl stop postgresql        # Linux
brew services stop postgresql@16      # macOS

Or, better, change the host port instead, so the two coexist:

docker run -d -p 15432:5432 postgres:17
psql -h localhost -p 15432 -U postgres

Only the left-hand number changes. The container still listens on 5432 internally, so nothing inside it or in your Compose network needs adjusting.

Fixing it properly in Compose

Hardcoded host ports are what make this recur. Make them configurable:

services:
  api:
    build: .
    ports:
      - "${API_PORT:-3000}:3000"
  db:
    image: postgres:17
    ports:
      - "${DB_PORT:-15432}:5432"
API_PORT=3001 docker compose up -d

Better still, do not publish ports you do not need from the host at all. Containers on the same Compose network reach each other by service name on the container port, so a database only needs publishing if you connect to it from your own machine:

services:
  api:
    ports:
      - "3000:3000"      # you browse this
  db:
    image: postgres:17
    # no ports. The api reaches it at db:5432

That removes the conflict entirely and is closer to how the stack runs in production.

For throwaway containers, let Docker choose:

docker run -d -P nginx
docker port <container>
80/tcp -> 0.0.0.0:32768

Bind to localhost, not everything

Worth changing while you are here:

docker run -d -p 127.0.0.1:3000:3000 myapp

-p 3000:3000 binds 0.0.0.0, exposing the container to your whole network, and on Linux, Docker writes iptables rules that bypass ufw and firewalld, so a port you believed was firewalled is reachable from the LAN. Prefixing 127.0.0.1: limits it to your own machine.

A checklist

  1. docker ps -a --format '…{{.Ports}}' | grep <port>. A container, running or exited?
  2. Exited container holding it → docker rm, not just docker stop.
  3. sudo lsof -i :<port> or sudo ss -tlnp, who owns it on the host?
  4. docker-proxy with no container → restart the Docker daemon.
  5. A host service → stop it, or change the host side of the mapping.
  6. docker compose ls. A project running from another directory?
  7. Long term: parameterise host ports, and stop publishing what you do not need.
  8. Bind 127.0.0.1: so containers are not exposed to your network.

Frequently Asked Questions

How do I find what is using a port that Docker says is already allocated?

Two commands cover it. docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' shows every container including stopped ones and what each publishes. If none of them holds the port, sudo lsof -i :3000 or sudo ss -tlnp | grep :3000 names the host process. A result of docker-proxy means Docker itself still has a reservation, usually from a stopped container that has not been removed.

Why is a port still in use after I stopped the container?

Because stopping a container does not always release Docker's port reservation; removing it does. Run docker rm <name> rather than only docker stop. If the port is still held with no container to explain it, lsof showing docker-proxy, the daemon's networking state is stale and restarting Docker clears it. On Docker Desktop that means restarting the application.

How do I run a container when the host port is taken by a local service?

Change the host side of the mapping rather than stopping the service: docker run -p 15432:5432 postgres:17 publishes to 15432 while the container still listens on 5432 internally. Nothing inside the container or elsewhere on its Docker network needs to change, and you connect with psql -h localhost -p 15432. This is usually better than shutting down a locally installed database you may want later.

Why does Compose keep hitting port conflicts?

Because host ports are global while Compose projects are namespaced. Two projects can both try to publish 3000, and a stack left running from another directory holds it. docker compose ls lists every running project with its config file path, which finds the culprit immediately. Parameterise the host port as "${API_PORT:-3000}:3000" so it can be overridden, and stop publishing services that only other containers need.

Do I need to publish a database port in Compose?

Only if you connect to it from your own machine. Containers on the same Compose network reach each other by service name on the container port, so an API talks to db:5432 whether or not 5432 is published to the host. Removing the ports entry for internal services eliminates this class of conflict entirely and matches how the stack runs in production, where nothing exposes a database publicly.

Is -p 3000:3000 safe on a shared network?

Not especially. That form binds 0.0.0.0, making the container reachable from anything that can route to your machine, and on Linux, Docker inserts iptables rules ahead of ufw and firewalld, so a port you believe is firewalled is in fact open on the LAN. Use -p 127.0.0.1:3000:3000 to bind only the loopback interface, which is what you almost always want for local development.

Learn the underlying concept

Other Docker errors