Scientyfic World

How to Fix and Avoid Docker Port Conflicts (Port Is Already Allocated)

You run docker run -p 8080:80 nginx and Docker answers with an error like port is already allocated or address already in use. Something on your machine already owns port...

Share:

Get an AI summary of this article

local port conflicts in docker

You run docker run -p 8080:80 nginx and Docker answers with an error like port is already allocated or address already in use. Something on your machine already owns port 8080. This guide explains what the error means, how to find the culprit in under a minute, and the fixes that actually work, including the ones that stay out of your way when you run many projects at once.

Updated October 2026: I rewrote this 2024 post. It still taught the old docker-compose command and version: '3' files (Compose files no longer need a version line), published a MySQL port to the whole network in its examples, and presented custom Docker networks as a fix for port conflicts. Networks do not fix host port conflicts, because only published ports use host ports. It also missed the most common surprise on Macs: AirPlay Receiver sitting on port 5000. The commands below are current, and the ones I could run locally, I did.

What a Port Conflict Is

A computer can have only one program listening on a given port and address at a time. When you publish a container port with -p HOST:CONTAINER, Docker must claim HOST on your machine. If something else already has it, the container fails to start. The container’s own port (the right-hand number) never conflicts across containers, since each container has its own network namespace; only the left-hand, host port can collide.

The two errors you will see most often:

  • Bind for 0.0.0.0:8080 failed: port is already allocated usually means another container already publishes that host port.
  • listen tcp4 0.0.0.0:8080: bind: address already in use usually means a non-Docker process on your machine has it.

Those are the typical messages, and the exact wording varies a little by Docker version and platform.

Step 1: Find Out Who Has the Port

Is it another container?

docker ps --filter "publish=8080"

This lists running containers that publish host port 8080. If one shows up, stop or remove it (docker stop NAME), or choose another port for the new one. Remember that stopped-but-existing containers do not hold ports, but a container stuck in a restart loop can.

Is it a program on your machine?

SystemCommand
macOSlsof -nP -iTCP:8080 -sTCP:LISTEN
Linuxsudo ss -ltnp 'sport = :8080'
Windows (PowerShell)Get-NetTCPConnection -LocalPort 8080 -State Listen, then Get-Process -Id <OwningProcess>

I ran the macOS command while a test server held port 8080, and it printed the process name and ID. In the same test, trying to bind that port from Python failed with OSError: [Errno 48] Address already in use, which is the same underlying error Docker reports. I did not run the Linux and Windows commands for this post, but they are the standard equivalents.

Common culprits that surprise people

  • macOS AirPlay Receiver uses ports 5000 and 7000. Port 5000 is the default for Flask and many sample apps. On my Mac, lsof showed ControlCenter listening on port 5000 right now. Either choose a different port, or switch off AirPlay Receiver in System Settings (the toggle is in the AirDrop & Handoff or Sharing pane, depending on your macOS version).
  • A local database or web server you forgot about. A native PostgreSQL on 5432, MySQL on 3306, Redis on 6379 or Apache/nginx on 80 are classic collisions with their container versions.
  • A previous run of your dev server that never exited. The lsof or ss output shows the process to stop.
  • Windows reserved port ranges. On Windows, a container can fail with “an attempt was made to access a socket in a way forbidden by its access permissions” even though nothing is listening. Windows (often with Hyper-V or WSL) can reserve ranges of ports. Check them with netsh interface ipv4 show excludedportrange protocol=tcp and pick a port outside those ranges. This is community-reported behaviour that I could not test here.

Fix 1: Choose a Different Host Port

The simplest fix is to change only the left number. The container still listens on 80; only the door you use on your machine changes:

docker run -d -p 8081:80 nginx

Open http://localhost:8081. Keep a short list of which port belongs to which project so you do not rediscover the conflict next month, and prefer ports in the 3000-9000 range, away from the system services.

Fix 2: Let Docker Pick a Free Port

If you do not care which port you get, leave out the host port. Docker picks an available one:

docker run -d -p 80 --name web nginx
docker port web

docker port web prints the mapping, for example 80/tcp -> 0.0.0.0:32768. The -P (capital) flag does the same for every port the image declares with EXPOSE. Per the Compose reference, giving only a container port in ports: also makes the engine pick a free host port. This is ideal for tests, CI and anything started by scripts, because it can never collide. It is a poor fit for a service you need to bookmark, since the port changes each run.

Fix 3: Make the Port Configurable in Compose

Hard-coded ports are what break the second copy of a project. Use an environment variable with a default, so each developer or environment can override it without editing the file:

services:
  web:
    image: nginx:stable-alpine
    ports:
      - "${WEB_PORT:-8080}:80"

  db:
    image: postgres:18-alpine
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    # no "ports:" here - the web service reaches it as db:5432 on the Compose network
    volumes:
      - dbdata:/var/lib/postgresql

secrets:
  db_password:
    file: ./db_password.txt

volumes:
  dbdata:
docker compose up -d                  # uses 8080
WEB_PORT=8090 docker compose up -d    # uses 8090
docker compose ps                     # shows the published ports

Notes on this file, which I validated as YAML:

  • The ${WEB_PORT:-8080} syntax means “use WEB_PORT, or 8080 if it is not set”, as the Compose file reference describes. You can also put WEB_PORT=8090 in a .env file next to compose.yaml.
  • Use docker compose (with a space), not the old docker-compose command, and leave out the version: line, which is obsolete.
  • The database has no ports: entry at all. The web service reaches it at db:5432 over the Compose network, so nothing on your machine needs port 5432, and nothing outside can connect. The original post published the database port, which both invites conflicts with a local database and exposes it. Publish a database port only when you really need to connect from a tool on your host, and then bind it to localhost (see below).
  • The Postgres image changed in version 18: per the official image documentation, the data volume is now mounted at /var/lib/postgresql (for 17 and below it was /var/lib/postgresql/data). Mounting at the wrong path means your data does not persist.
  • Compose also gives each project its own network, named after the project directory, so two copies of a project in different folders do not clash on container names or networks, as long as you do not set a fixed container_name. Only host ports can still collide.

Fix 4: Bind to localhost Only

By default, publishing a port makes it reachable on all of your machine’s network addresses. Docker’s port publishing documentation says that, when no host address is given, ports are published on all addresses (0.0.0.0 and [::]), and it describes publishing as insecure by default for that reason. If a service is only for you, say so:

docker run -d -p 127.0.0.1:8080:80 nginx
services:
  web:
    image: nginx:stable-alpine
    ports:
      - "127.0.0.1:8080:80"

Binding to 127.0.0.1 does not make a conflict go away (a different program using 8080 on all addresses still collides), but it removes the exposure to other computers, and it lets two services share a port number on different addresses. One more warning from Docker’s own documentation: Docker manipulates firewall rules in a way that can bypass tools like ufw, so a ufw rule alone may not block a published port. See the packet filtering and firewalls page. Binding to localhost is the safe default on a server.

Fix 5: Run Several Copies of a Service

Scaling a service that has a single fixed host port cannot work: the second copy tries to claim the same port. Give Compose a range of host ports, and each replica takes the next free one:

services:
  web:
    image: nginx:stable-alpine
    ports:
      - "8080-8082:80"
docker compose up -d --scale web=3
docker compose ps

The short syntax allows ranges such as "8080-8082:80". This is fine for local experiments. For real load balancing, put a proxy in front (next section) rather than asking clients to pick a port. I validated the file’s syntax but did not run the scale command here.

Fix 6: Put a Reverse Proxy in Front

When you have many web services, stop publishing a port for each. Run one proxy that owns ports 80 and 443 and routes by host name; every other container publishes nothing and is reached over the Docker network. This is the pattern that scales:

services:
  proxy:
    image: caddy:2-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data

  app:
    image: ghcr.io/your-user/app:latest   # no published ports

  api:
    image: ghcr.io/your-user/api:latest   # no published ports

volumes:
  caddy_data:
app.example.com {
	reverse_proxy app:3000
}

api.example.com {
	reverse_proxy api:8000
}

Caddy forwards app.example.com to app:3000 and api.example.com to api:8000, using Compose service names as host names (see the reverse_proxy directive). It also obtains HTTPS certificates automatically for real domains. I ran caddy validate on this Caddyfile and it is valid, and I checked the Compose file parses; I did not start the stack. For local development you can use names like app.localhost. Nginx, Traefik and other proxies work the same way: one published entry point, many unpublished services behind it.

What Does Not Fix It

  • Custom Docker networks. They are great for isolating services and letting containers find each other by name, but they do not change host port usage. A conflict is between a published port and another program on the host.
  • EXPOSE in a Dockerfile. It documents which port the app listens on. It does not publish anything and cannot conflict.
  • Restarting Docker. It sometimes frees a stuck port, but it is a guess. Find the owner with the commands above.

Quick Checklist

  1. Read the error: allocated usually means another container, address already in use usually means a host process.
  2. Run docker ps --filter "publish=PORT" and the system command for your OS.
  3. Stop the owner, or change your host port (the left number).
  4. In Compose, use ${VAR:-default} for ports and do not publish databases unless you must.
  5. Bind to 127.0.0.1 for anything that is only for you.
  6. For many web services, use one reverse proxy and publish only 80 and 443.

Conclusion

Port conflicts look mysterious but have one cause: two things want the same host port. Find the owner, move your side to a free port, and reduce how many host ports you use at all by keeping databases and internal services unpublished and fronting web apps with a single proxy. Make ports configurable in Compose, and a second copy of your project stops being a problem. If you are wiring Docker into automated deployments, see how to build a CI/CD pipeline with Docker for a deployment that publishes its port on localhost behind a proxy.

What does “port is already allocated” mean in Docker?

Another container is already publishing the same host port. Find it with docker ps –filter “publish=PORT”, stop it or choose a different host port for the new container.

What does “address already in use” mean in Docker?

A program outside Docker is already listening on that port on your machine. Find it with lsof on macOS, ss on Linux or Get-NetTCPConnection on Windows, then stop it or use another host port.

Why does port 5000 not work on my Mac?

macOS AirPlay Receiver listens on ports 5000 and 7000 when it is enabled. Use a different host port, or turn off AirPlay Receiver in System Settings.

Can I run two containers on the same port?

Two containers can both listen on port 80 inside their own networks, but only one can publish a given host port on the same address. Publish them on different host ports, or put a reverse proxy in front and route by host name.

How do I let Docker choose a free port?

Use -p CONTAINER_PORT without a host port (for example -p 80), or -P to publish all exposed ports. Then run docker port NAME to see the assigned ports. In Compose, list only the container port under ports.

Snehasish Konger
Developed @scientyficworld.org | Technical writer @Nected | Content Developer
Connect with Snehasish Konger

On This page

Take a Pause with Intervals

A Sunday letter on building, writing, and thinking deeper as a developer — short, honest, and worth your time.

Snehasish Konger profile photo

"Hey there — I'm Snehasish. Hope this post saved you some head-scratching time! I've spent years turning technical chaos into clarity, and I'm here to be your guide through the maze of modern tech. Stick around for more lightbulb moments — we're just getting started."

Related Posts