ports are not available: exposing port TCP 127.0.0.1:15900 ... bind: Only one usage of each socket address (protocol/network address/port) is normally permitted. means a Windows program, not a container, is already listening on that host port. Find the process that owns the port, stop it or publish a different port, and start the container again.

Get-NetTCPConnection -LocalPort 15900 -State Listen or netstat -ano prints the owning process id. Once that process had stopped, docker start on the same container came up with the port published.

The error

PS> docker run -d --name fix9-run-web -p 127.0.0.1:15900:80 alpine sleep 300
51478a6abbfe9e1aea1f54efa0401b811c963ae98d1ee573ca489d3dd8a32400
docker: Error response from daemon: ports are not available: exposing port TCP 127.0.0.1:15900 -> 127.0.0.1:0: listen tcp4 127.0.0.1:15900: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
...
Run 'docker run --help' for more information

Why it happens

The last part of the message is the Windows socket error text, the one any Windows program gets when it binds an address and port that another socket already holds. In this test the holder was a PowerShell process with a TcpListener on 127.0.0.1:15900, started on purpose. Docker reported the bind failure and exited with code 125.

The container was not thrown away: docker ps -a listed fix9-run-web as Created. That matters for the next attempt, because running the same docker run again failed on the name, not on the port: Conflict. The container name "/fix9-run-web" is already in use. Two related messages have their own posts: a port held by another container gives port is already allocated, and a port inside a range Windows has reserved gives forbidden by its access permissions.

The fix

Find the owner of the port. Both commands gave the same process id:

PS> Get-NetTCPConnection -LocalPort 15900 -State Listen | Format-Table LocalAddress,LocalPort,State,OwningProcess -AutoSize | Out-String

LocalAddress LocalPort  State OwningProcess
------------ ---------  ----- -------------
127.0.0.1        15900 Listen         21976

PS> netstat -ano | findstr :15900
  TCP    127.0.0.1:15900        0.0.0.0:0              LISTENING       21976

PS> Get-Process -Id 21976 | Format-Table Id,ProcessName -AutoSize | Out-String

   Id ProcessName
   -- -----------
21976 powershell

Stop that process (here it was the test listener, so stopping it was safe), then start the container that was left behind:

PS> Stop-Process -Id 21976

PS> docker start fix9-run-web
fix9-run-web

PS> docker ps -a --filter name=fix9-run-web --format "{{.Names}} {{.Status}} {{.Ports}}"
fix9-run-web Up Less than a second 127.0.0.1:15900->80/tcp

If the owner is something you need, such as a database or a dev server, leave it running and publish another host port instead, for example -p 127.0.0.1:15901:80. Remove the Created container first (docker rm fix9-run-web) or give the new one another name. Check what a process is with Get-Process -Id before you stop it.

How it was reproduced

On Windows 11 with Docker Desktop, engine and client 29.9.3, and the cached alpine image. A separate powershell.exe started a System.Net.Sockets.TcpListener on 127.0.0.1:15900; docker run -p 127.0.0.1:15900:80 then failed with the message above. The owner was found with Get-NetTCPConnection and netstat -ano, stopped by its process id, and docker start brought the same container up with the port mapped. The container is the test's own and was removed afterwards.

Frequently asked

How do I find which process is using a port on Windows?
Run Get-NetTCPConnection -LocalPort 15900 -State Listen in PowerShell, or netstat -ano piped to findstr :15900. Both show the owning process id; Get-Process -Id with that number shows its name.
Why does docker ps -a show my container as Created after the port error?
In this test docker run had already created the container when the bind failed, and docker ps -a listed it as Created. Start it with docker start once the port is free, or remove it before running the same command again.
Is this the same as 'port is already allocated'?
No. In this test the port was held by a Windows process and Docker printed the Windows socket error. A port held by another container gave a different message in the same test, Bind for 127.0.0.1:15901 failed: port is already allocated. In that case docker ps lists which container publishes that port.

More decoded errors in the Fixes category. For how Docker Desktop publishes ports on Windows, see Docker for .NET developers, ports and networking.