Docker Error Guide: 'ports are not available: exposing port' — Fix Port Binding
Fix Docker 'ports are not available: exposing port TCP': release ports held by the OS, reserved dynamic-port ranges, host firewalls, and Desktop networking so container port publishing succeeds.
- #docker
- #troubleshooting
- #errors
- #networking
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
ports are not available: exposing port means Docker asked the host operating system to bind a published port and the OS refused. Unlike port is already allocated (which Docker raises from its own bookkeeping), this error comes back from the host network stack — the port is held by another process, falls inside a reserved dynamic range, or is blocked by the platform’s networking layer (common on Docker Desktop for Windows/macOS with the WSL2 or hypervisor proxy).
The literal message names the port and the OS-level cause:
Error response from daemon: Ports are not available: exposing port TCP 0.0.0.0:50051 -> 127.0.0.1:0: listen tcp 0.0.0.0:50051: bind: An attempt was made to access a socket in a way forbidden by its access permissions.
On Linux the underlying reason is typically:
Error response from daemon: ports are not available: exposing port TCP 0.0.0.0:8080 -> 0.0.0.0:0: listen tcp 0.0.0.0:8080: bind: address already in use
Symptoms
docker run -pordocker compose upfails at container start withports are not available: exposing port.- The port is not published by any other container, yet the bind still fails (a non-Docker process or an OS reservation holds it).
- On Windows, ports in certain ranges fail intermittently after reboots (Hyper-V/WinNAT reserved ranges).
- A privileged low port (<1024) fails when the daemon or user lacks permission.
Common Root Causes
- A non-Docker process owns the port — a locally running service, IDE, or system daemon already listening on it.
- Windows dynamic-port / Hyper-V reservations —
winnatreserves ranges of ephemeral ports; a published port landing inside one is refused by the OS. - Privileged port without permission — binding a port below 1024 when running rootless or without the capability.
- Host firewall / security software blocking the bind.
- Docker Desktop networking stuck — the vpnkit/WSL2 proxy holding stale reservations after sleep/resume.
- Another container on the same host port — the classic collision, surfaced here as an OS-level bind failure.
Diagnostic Workflow
Find what is holding the port on Linux:
sudo ss -ltnp | grep ':8080'
sudo lsof -iTCP:8080 -sTCP:LISTEN
On Windows, check the reserved dynamic-port ranges that WinNAT excludes:
netsh interface ipv4 show excludedportrange protocol=tcp
netstat -ano | findstr :50051
Confirm no Docker container already publishes it:
docker ps --format '{{.Names}}\t{{.Ports}}' | grep 8080
Read the daemon’s precise reason:
journalctl -u docker --since '5 min ago' | grep -i 'ports are not available\|bind:'
Example Root Cause Analysis
A Windows developer’s gRPC container failed after a laptop reboot with:
Ports are not available: exposing port TCP 0.0.0.0:50051 ... bind: An attempt was made to access a socket in a way forbidden by its access permissions.
Nothing was listening on 50051 (netstat -ano | findstr :50051 was empty), so it was not a normal collision. netsh interface ipv4 show excludedportrange protocol=tcp revealed that Hyper-V’s WinNAT had reserved a dynamic range that swallowed 50051 after the reboot — the OS itself was forbidding the bind.
The fix was to restart the Windows NAT service (net stop winnat then net start winnat), which released the stale reservation, after which the container published cleanly. For a durable fix, the team moved the service to a fixed port outside the reserved ephemeral ranges and, where needed, added an explicit exclusion so the OS would not reclaim it.
Prevention Best Practices
- Pick stable ports outside the ephemeral range (avoid the OS dynamic-port window, typically 49152–65535) for long-lived published services.
- On Windows, reserve or exclude fixed ports so WinNAT will not reclaim them after reboots (
netsh int ipv4 add excludedportrange). - Check for host listeners before publishing in scripts (
ss -ltnp/netstat) and fail fast with a clear message. - Avoid privileged low ports unless necessary; publish high and front with a reverse proxy.
- Restart Docker Desktop / winnat after sleep-resume issues that leave stale reservations.
- Standardize a port map across the team so two services never claim the same host port.
Quick Command Reference
sudo ss -ltnp | grep ':8080' # Linux: who holds the port
sudo lsof -iTCP:8080 -sTCP:LISTEN # alternative listener lookup
docker ps --format '{{.Ports}}' # ports Docker already publishes
# Windows:
netsh interface ipv4 show excludedportrange protocol=tcp
net stop winnat && net start winnat # release stale reservations
netstat -ano | findstr :50051 # find the PID on Windows
Conclusion
ports are not available: exposing port is the host OS — not Docker’s allocator — refusing the bind. Identify the real holder: a non-Docker listener, a reserved dynamic-port range (especially on Windows), a privileged-port restriction, or firewall software. Choose stable ports outside the ephemeral range, exclude them from OS dynamic reservations, and check for listeners before publishing. For the daemon-side collision variant, see the port is already allocated guide and the full Docker guides.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.