ports are not available: exposing port TCP 0.0.0.0:49400 ... bind: An attempt was made to access a socket in a way forbidden by its access permissions. — Windows has reserved that host port, so Docker cannot open it even though nothing is listening there. List the reserved ranges with netsh and publish a port outside all of them.

No administrator prompt and no service restart are needed for this. Remove the container the failed run left behind and run it again with a host port that is not in any listed range; on this machine 15434 worked first time.

The error

$ docker run -d --name fix3-dpg-excl -e POSTGRES_PASSWORD=dev-only-password -p 49400:5432 postgres:18
7a3702d0740ff51c77b88cc86e7e5841c4041ea417afbcae8f5476d0b55f92f2
docker: Error response from daemon: ports are not available: exposing port TCP 0.0.0.0:49400 -> 127.0.0.1:0: listen tcp 0.0.0.0:49400: bind: An attempt was made to access a socket in a way forbidden by its access permissions.

Why it happens

Docker Desktop opens a published port on the Windows side itself; the message shows it (listen tcp 0.0.0.0:49400). Windows keeps a list of TCP port ranges that programs may not bind, and a bind inside one fails with this access-permissions error. It is not a conflict with another program: Get-NetTCPConnection -LocalPort 49400 found nothing on that port. The port is reserved, not in use.

This is the list on the machine that produced the error:

> netsh interface ipv4 show excludedportrange protocol=tcp

Protocol tcp Port Exclusion Ranges

Start Port    End Port
----------    --------
        80          80
     49382       49481
     49680       49779
     50000       50059     *
     57388       57487
     57955       58054
     58055       58154
     58155       58254
     60278       60377
     61542       61641

* - Administered port exclusions.

Port 49400 sits in the first block of 100. Nine of the ten entries lie inside the Windows dynamic port range, which netsh int ipv4 show dynamicport tcp reported as 16384 ports from 49152. The asterisk marks an administered exclusion; for the rest, netsh does not say which program made the reservation, so this post does not guess. Your list will be your own, so check it rather than copying port numbers from anywhere.

The fix

docker container rm fix3-dpg-excl
docker run -d --name fix3-dpg-excl -e POSTGRES_PASSWORD=dev-only-password -p 15434:5432 postgres:18

The failed run leaves the container in the Created state, so it is removed first. The new one came up with 0.0.0.0:15434->5432/tcp, and psql connected through it. In a compose file, change the left-hand side of the ports: entry the same way. Removing or moving a reserved range needs an administrator prompt and changes the whole machine; it was not tried here, and for a development container a different port is the simpler answer.

How it was reproduced

Windows 11 (build 26200), Docker Desktop 4.91.0 with engine 29.8.0 on the WSL 2 backend, image postgres:18 (18.4). netsh was run from a normal, non-elevated prompt and listed the ranges above. A container publishing host port 49400, inside the 49382 to 49481 block, failed with the message above while nothing listened on that port. The same container on 15434 started and accepted a psql connection from a client container through host.docker.internal. No range was added, removed or changed. The name fix3-dpg-excl is this test's own container; use yours.

Frequently asked

How do I see which ports Windows has reserved?
Run netsh interface ipv4 show excludedportrange protocol=tcp. It lists every excluded TCP range, marks administered exclusions with an asterisk, and works from a normal prompt without administrator rights.
Why does Docker say ports are not available when nothing is using the port?
Because the port is reserved, not used. Windows refuses a bind inside an excluded range even when no program listens there, and Docker Desktop reports that refusal as an access-permissions error.
Do I need to restart winnat or change Windows settings to fix this Docker error?
Not for a development container. Publishing a host port outside every listed range fixed it here without administrator rights or restarts. Changing reserved ranges or stopping services affects the whole machine and was not tested for this post.

More decoded errors in the Fixes category. When another container already publishes the port, Docker says port is already allocated instead.