Troubleshooting
Docker & Compose

Fix Docker "Port Is Already Allocated" for Django Compose Stacks

Fix Docker Compose errors Bind for 0.0.0.0:8000 failed port is already allocated and address already in use for Django, Postgres, and Redis: find the process or container holding the port, remove orphans, and stop publishing ports you do not need.

Problem statement

docker compose up fails before your Django container starts:

Error response from daemon: driver failed programming external connectivity on endpoint
myapp-web-1: Bind for 0.0.0.0:8000 failed: port is already allocated

or, for the database:

Error starting userland proxy: listen tcp4 0.0.0.0:5432: bind: address already in use

A published port (ports: ["8000:8000"]) asks Docker to listen on that port on the host. Only one process can listen on a host port. "Port is already allocated" means another container already published it; "address already in use" means a non-Docker process, such as a local PostgreSQL or a runserver, holds it.

Why the bind failsCompose publishes8000:8000Docker bindshost port 8000Already takencontainer or processNot startedbind failedOnly one listener per host port; find who holds it or stop publishing it.
  1. Find the holder: docker ps --filter publish=8000 for containers, sudo ss -ltnp 'sport = :8000' for processes.
  2. Stop the old stack: docker compose down --remove-orphans in the other project directory.
  3. Stop local services that clash: sudo systemctl stop postgresql or redis-server.
  4. Change the host side of the mapping: "8001:8000".
  5. In production, publish only the reverse proxy; let Django, Postgres, and Redis talk over the Compose network.

Step-by-step solution

1. Identify what holds the port

docker ps --format '{{.Names}}\t{{.Ports}}' | grep 8000
sudo ss -ltnp | grep ':8000 '

A container in the list means another Compose project, an older run of this one with a different project name, or a container started with docker run. A process such as python manage.py runserver, postgres, or redis-server in the ss output means a host service.

2. Clean up old containers

Renaming the project folder or the name: in compose.yaml changes the project name, so docker compose down in the new folder does not see the old containers. Remove them by name:

docker ps -a --filter "name=myapp" --format '{{.Names}}'
docker rm -f old-myapp-web-1 old-myapp-db-1

--remove-orphans cleans up containers from services you deleted from the file.

match the holderHolderCause and fixcontainer from another dirold project name; docker rm -f itpostgres or redis processhost service installed;stop or unpublishpython runserverforgotten dev server; stop itnothing visiblerootless or other netns;check docker contextThe message differs: allocated means a container, in use means a processdjango-deployment.com
Fix the holder rather than picking random ports until one works.

3. Stop publishing internal services

In production, the database and Redis do not need host ports at all. Containers on the same Compose network reach each other by service name:

services:
  db:
    image: postgres:17
    # no ports: section; web connects to db:5432
  redis:
    image: redis:7
  web:
    build: .
    environment:
      DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/app
      REDIS_URL: redis://redis:6379/0
  proxy:
    image: caddy:2
    ports: ["80:80", "443:443"]

Only the proxy publishes ports. This avoids clashes and keeps the database off the public interface, which ports: ["5432:5432"] exposes on 0.0.0.0, bypassing UFW rules on many systems.

4. Use different ports per environment

For local development with several projects, keep internal ports the same and change only the host side:

# compose.override.yaml
services:
  web:
    ports: ["127.0.0.1:8001:8000"]
  db:
    ports: ["127.0.0.1:55432:5432"]

Binding to 127.0.0.1 keeps dev ports private to your machine.

5. Restart Docker if the port is stuck

Rarely, Docker's proxy keeps a port after a crash. If nothing holds it but the error persists, restart the daemon: sudo systemctl restart docker. This restarts all containers on the host.

ports in a production Compose fileEvery service publishes✕clashes with host services✕database on 0.0.0.0✕bypasses UFW rulesOnly the proxy publishes✓two host ports total✓services use network names✓nothing internal exposedService names on the Compose network replace most published portsdjango-deployment.com
Publish database ports only on 127.0.0.1 and only when you need host access.

Explanation

Publishing a port makes Docker add NAT rules and, in many setups, start a small proxy that listens on the host port. Like any listener, it needs exclusive use of that port on that address. Docker tracks ports it allocated to other containers and reports "already allocated" for those; for ports held by other programs, the kernel returns EADDRINUSE, shown as "address already in use". Containers talking to each other over a user-defined network do not use host ports at all.

Edge cases / notes

  • IPv6. A process on [::]:8000 can block 0.0.0.0:8000 on systems where IPv6 sockets also accept IPv4.
  • Docker Desktop. On macOS and Windows, the port is held by the Docker Desktop VM process; lsof -i :8000 shows com.docker.backend.
  • Swarm and Kubernetes. These have their own port publishing models; host port clashes appear differently there.

Next steps

FAQ

What is the difference between "allocated" and "address already in use"?

"Allocated" means another container published the port. "Address already in use" means a process outside Docker is listening on it.

Does Postgres need ports: in Compose?

Not for Django in the same Compose project. Use the service name db as the host. Publish only if tools on the host must connect, and bind to 127.0.0.1.

Why does the old stack still run after I renamed the folder?

The project name changed, so Compose treats it as a different project. Remove the old containers by name.

Can two containers share a host port?

No. Put a reverse proxy in front and route by host name or path instead.

Keep going