What was actually holding the port
The port was held by the OS, not a process. Our /healthz listener on 0.0.0.0:8088 stopped binding on a Windows host running Docker Desktop with WSL2:
bind: An attempt was made to access a socket
in a way forbidden by its access permissions.
The signal that misleads you: netstat -ano showed 8088 with nothing listening. The port was not listened on — it was excluded. Hyper-V’s Host Networking Service (HNS) reserves ports in 100-port blocks for the container NAT layer, and one block had swallowed 8088:
netsh interface ipv4 show excludedportrange protocol=tcp
# Start End
# 8050 8149 * <- HNS block; asterisk = admin reservation
That gap between the two tools is the whole diagnosis. The port is free to every connection-table tool and forbidden to bind(). These HNS reservations are non-deterministic across reboots (docker/for-win #3171), which matches the symptom exactly: 8088 bound fine the day before, and nothing in our stack changed.
The fix
Picking a “safe” port number is no defense. 8088 already sits below the dynamic client range (49152–65535, per MS Learn), and HNS reserved it anyway. The only durable protection is to claim the port yourself, persistently, before HNS can:
netsh int ipv4 add excludedportrange protocol=tcp `
startport=8088 numberofports=1 store=persistent
An administrative exclusion (the * block above) is protected from HNS reuse (pomeroy.me), and store=persistent carries it across reboots. Run it once in host bootstrap — not at app startup, where it races the very reservation it’s meant to win.
Lesson: a listener that can’t bind fails worse than one returning 503 — there is no error to catch downstream, only absence. On bind error, log the target port and the current excludedportrange state, then exit non-zero. Retrying into the same wall only delays the diagnosis.