Fix Postgres 5432 bind: address already in use in Docker
Tell the Docker messages apart, find what owns port 5432 on Linux, macOS or Windows, and pick between stopping it and moving the published port.
TL;DR#
bind: address already in use on port 5432 means that, when Docker tried to publish the container's port on the host, something on the host was already listening on 5432. On a developer machine that is almost always a PostgreSQL server installed directly on the OS. Find the owner, then either stop it or publish the container on another host port, such as 5433:5432. If the message instead says port is already allocated, the owner is another container. Look for it with docker ps. On Docker Desktop for Windows, a Windows program that holds the port gives a third message, ports are not available, or no error at all, so check the port even when the container starts.
The report on Stack Overflow comes from a Compose file with ports: - '5432:5432' on a Docker release whose message read Error starting userland proxy:
ERROR: for postgres Cannot start service postgres: driver failed programming external connectivity on endpoint pg-db (b3e5697cd563264250479682ec83d4a232d0d4bd679a787ad2089e944dda9e2f): Error starting userland proxy: listen tcp 0.0.0.0:5432: bind: address already in useRecent Docker Engine releases word it differently. Reproduced on 4 October with Docker Engine 28.5.2 on Linux, while another process listened on 5432:
docker: Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint pg-e (0ee0537e4011c80e70cc9df9064ec16fa2e1ef2bb287400c9f62736aad134217): failed to bind host port for 0.0.0.0:5432:172.18.0.2:5432/tcp: address already in useWhy Docker cannot publish the port#
ports: - '5432:5432' means "listen on port 5432 of the host and forward to port 5432 of the container". The second number belongs to the container, and nothing else uses it. The first number is a real socket on your machine, shared with every other program. The Docker port publishing documentation describes the HOST:CONTAINER form. On a Linux host, the engine cannot bind a host port that another listener already holds on the same address, so it refuses to start the container rather than share the port.
The error ends in one of two ways, and they point to different owners. Both were reproduced on the same Linux engine:
- A process outside Docker owns the port. With a listener running on 5432, the container failed with
address already in use, as above. Stopping that listener was the only change, and the samedocker run -p 5432:5432then started. - Another container already publishes it. With one container on
5432:5432, a second one asking for the same mapping failed with:
docker: Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint pg-g (316170cd87d60b757c857e02fe5f5e1c2441b0d002fde258735afd2ea61d4332): Bind for 0.0.0.0:5432 failed: port is already allocatedThe two endings come from two different checks. port is already allocated comes from the engine's own record of the host ports its containers publish (the port allocator in the Docker Engine source). address already in use is the operating system refusing the bind, so it also appears when the owner is a container whose port Docker did not publish, such as one started with --network host (network_mode: host in Compose) that listens on the port. Docker Engine 29.7.2 also shortens the end of that message. Reproduced on 6 October on the engine inside Docker Desktop, with a host-network container listening on test port 55434:
docker: Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint aotd-audit-5432-l1 (355f25f351e0c105dbd5d23a603b088022eb2fa2c377aeccb69b20a9fe93dea8): failed to bind host port 0.0.0.0:55434/tcp: address already in useDocker Desktop 4.89.0 on Windows 11 (Docker Engine 29.7.2) gave the same port is already allocated wording for the container case. A Windows program that owns the port is different, and the result depends on how that program listens. Reproduced on 6 October with small test listeners on port 55433, each running as the signed-in Windows user:
- A program listening on
[::], IPv6 and IPv4 together, madedocker runfail with Docker Desktop's own message. The test machine runs Windows in French. On an English system the end of the line is the standard text of Windows error 10048, "Only one usage of each socket address (protocol/network address/port) is normally permitted" (system error codes):
docker: Error response from daemon: ports are not available: exposing port TCP 0.0.0.0:55433 -> 127.0.0.1:0: listen tcp 0.0.0.0:55433: bind: Une seule utilisation de chaque adresse de socket (protocole/adresse réseau/port) est habituellement autorisée.- A program listening on
0.0.0.0only, with or without exclusive use of the address, or on0.0.0.0plus a separate IPv6-only socket, let the container start without any error. Both then listened on the port: connections to127.0.0.1still reached the Windows program, and only::1reached the container.
Windows applies different binding rules when the first program runs under another account, as a PostgreSQL service does, and the tests did not cover that case. On Docker Desktop for Windows, a clean start therefore does not prove that the host port is free, so run step 1 even when nothing failed.
The top answer (score 40) names the usual outside owner: PostgreSQL installed on the machine and started as a service, which listens on 5432 by default. Linux packages and the Windows installer usually register it as a service started at boot, so it is easy to forget that it is there.
Fix#
1. Find what owns port 5432#
On Linux, run it with sudo, because the database service runs under its own account:
sudo ss -ltnp 'sport = :5432'On macOS:
sudo lsof -nP -iTCP:5432 -sTCP:LISTENOn Windows, in PowerShell:
Get-NetTCPConnection -LocalPort 5432 -State Listen |
Select-Object LocalAddress, LocalPort, OwningProcess,
@{n='Process'; e={ (Get-Process -Id $_.OwningProcess).ProcessName }}On the Windows test machine, that command showed the port held by Docker Desktop itself, because another container published it:
LocalAddress LocalPort OwningProcess Process
127.0.0.1 5432 26668 com.docker.backendWhen the owner is Docker (com.docker.backend on Docker Desktop, or docker-proxy on Linux), list the containers that publish the port:
docker ps --filter publish=5432 --format '{{.Names}} {{.Ports}}'A container started with --network host publishes nothing (Docker's host network docs: published ports are discarded in that mode), so this filter misses it; plain docker ps lists it with an empty PORTS column.
On Docker Desktop for Windows, wslrelay may also appear, on ::1: in the tests above it came and went with the container. Any other program in the list holds the port from outside Docker, even when the container started.
2a. The owner is a local PostgreSQL you do not need: stop it#
On a Debian or Ubuntu machine where PostgreSQL runs as a systemd service:
sudo systemctl stop postgresql
sudo systemctl disable postgresqldisable keeps it from starting again at boot; without it, the conflict returns after the next restart. The top answer gives the older SysV equivalent, sudo service postgresql stop followed by sudo update-rc.d postgresql disable. On Windows, stop the PostgreSQL service in services.msc and set it to Manual. On macOS, stop it the way it was installed: brew services stop with the formula name for Homebrew, or the app's own control for Postgres.app.
2b. You need both databases: publish the container on another host port#
services:
postgres:
image: postgres:18
environment:
POSTGRES_PASSWORD: change-me
ports:
- '5433:5432'
volumes:
- pgdata:/var/lib/postgresql
volumes:
pgdata:In the reproduction, giving the second container another host port was the only change, and it started next to the first one:
pg-g 0.0.0.0:5433->5432/tcp, [::]:5433->5432/tcp
pg-e 0.0.0.0:5432->5432/tcp, [::]:5432->5432/tcpAs the top answer says, there is nothing wrong with this. Other services in the same Compose file still reach the database on postgres:5432, because service-to-service traffic on the Compose network uses the container port, not the host port. Only tools on your host change, such as psql -h localhost -p 5433 -U postgres. An app container that uses its own loopback address instead of the service name fails at connect time, which is a separate problem covered in the Compose connection refused fix.
The volume line uses /var/lib/postgresql, the volume the postgres:18 image declares; its data directory sits below it, in /var/lib/postgresql/18/docker. The asker's file used an anonymous volume and postgres:10.0. Keep a named volume. With an anonymous one, the next docker compose up after a docker compose down starts on a new, empty volume: the old one stays on disk, detached, and Compose does not mount it again. Reproduced with postgres:18 on 6 October: a table created before down was gone after up (relation "keep_me" does not exist), while the same steps with the named volume above kept it.
2c. The owner is another container: stop it or move one of them#
docker ps --filter publish=5432 --format '{{.Names}} {{.Ports}}'
docker stop old-project-postgres-1Replace old-project-postgres-1 with the name the first command printed. This is common when an old Compose project, started from another folder, is still running. docker compose down in that project's folder removes it cleanly; if that project has no named volume, its next up starts on an empty database, as in 2b.
Verify the fix#
docker compose up -d postgres
docker compose ps
psql -h localhost -p 5432 -U postgres -c 'select version();'docker compose ps should show the service as running, with 0.0.0.0:5432->5432/tcp (or 5433 if you moved it) in the ports column. The psql line should print a version string from the container. Use -p 5433 if you took step 2b.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
When the right server answers on the wrong port#
The dangerous case is not the error but its silent cousin. If you moved the container to 5433 and a script still connects to localhost:5432, it reaches the host's PostgreSQL, which has different users, passwords and databases. A seed script like the asker's would then seed the wrong server. Check which server you are talking to with psql -h localhost -p 5432 -U postgres -c 'show data_directory;' (reading this setting needs a superuser such as postgres, or a role granted pg_read_all_settings). The official postgres:18 image sets its data directory to /var/lib/postgresql/18/docker (tags before 18 report /var/lib/postgresql/data, and a PGDATA set in Compose overrides both); any other path means you reached a different server.
Docker Desktop for Windows has a variant that needs no port change. In the tests above, while a Windows program listened on 0.0.0.0, the container still started on the same port, 127.0.0.1 kept reaching that program, and only ::1 reached the container. Run the same show data_directory check with the exact host your app uses, 127.0.0.1 or localhost.
If psql without -h now fails with Peer authentication failed for user "postgres", it connected to the native server over its local socket. That error has its own fix in peer authentication failed for user postgres. If an ORM then rejects the new URL with an invalid port number although 5433 is correct, an unescaped character in the password is usually the cause. Prisma can't connect to PostgreSQL shows how to encode it.
Port 5432 is a convention, not a requirement. Pick the owner you want on the host, stop or move the other one, and keep connections inside Compose on the service name, where the host port never matters.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
PostgreSQL 18 Docker: Fix Existing Volume Startup Errors
Separate a Docker mount failure from a PostgreSQL major upgrade, preserve the original volume and prove the restored database survives a restart.
Fix ECONNREFUSED 127.0.0.1:5432 in Docker Compose (Node)
Your Node app and Postgres both start, yet every query fails with connection refused. Read the address in the error, fix the host, and gate startup on pg_isready.
Fix ECONNREFUSED 127.0.0.1:5432 in GitHub Actions
Your Postgres service is healthy, yet the migration step cannot reach it. Pick the host that matches how the job runs, and make Sequelize read DATABASE_URL.


