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.
- Find the holder:
docker ps --filter publish=8000for containers,sudo ss -ltnp 'sport = :8000'for processes. - Stop the old stack:
docker compose down --remove-orphansin the other project directory. - Stop local services that clash:
sudo systemctl stop postgresqlorredis-server. - Change the host side of the mapping:
"8001:8000". - 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.
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.
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
[::]:8000can block0.0.0.0:8000on 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 :8000showscom.docker.backend. - Swarm and Kubernetes. These have their own port publishing models; host port clashes appear differently there.
Next steps
- The non-Docker version is Fix Gunicorn Address Already in Use.
- Nginx on the host clashing with a container? See Fix Nginx Bind Port 80 Address in Use.
- Structure dev and prod ports with Configure Docker Compose Override for Django.
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.