The Symptom: TCP Works Flawlessly, UDP Goes to /dev/null
Here’s a scenario that’ll make any engineer want to throw their laptop out the window.
You’ve got your Docker container running. The -p 5005:5005/udp flag is set correctly. TCP port forwarding? Works like a charm. But UDP packets? They vanish into thin air.
The real kicker? The exact same image runs perfectly on a Linux host. Move it to Windows 10, and suddenly UDP is dead.
I hit this last week deploying an SNMP trap receiver (snmptrapd). Fired up Wireshark, sent a UDP packet from Python to 127.0.0.1:5005 — and Wireshark showed nothing. Not a single byte. The packet was swallowed by the void.
This isn’t a config typo. This is a known, chronic issue with Docker Desktop on Windows.
Root Cause: WinNAT vs. Loopback — A Marriage Made in Hell
The problem lives deep in Windows’ networking stack.
Docker Desktop on Windows relies on WinNAT (Windows Network Address Translation) to handle port mapping. WinNAT handles TCP reasonably well, but its UDP support is… let’s say “incomplete.”
Here’s the specific failure mode: when you expose a UDP port with -p 5005:5005/udp, Docker creates a NAT rule in WinNAT. But WinNAT refuses to forward UDP traffic destined for 127.0.0.1.
So the behavior breaks down like this:
- UDP packets from an external IP → works (physical NIC → WinNAT → container)
- UDP packets from localhost to
127.0.0.1:5005→ completely dead (WinNAT ignores them)
Someone on Reddit confirmed this with Wireshark captures: sending UDP to 127.0.0.1:5005 from the host machine produces zero captured packets. It’s not a firewall issue — the traffic never even reaches WinNAT’s forwarding path.
There’s a second, even nastier variant: the WinNAT service (winnat) occasionally hangs, killing all UDP port mappings while TCP continues working fine. This became more frequent after certain Windows 10 updates.
The Fix: Five Approaches, From Quick Reboot to Full Architecture Change
Option 1: Restart WinNAT Service (Fastest, But Temporary)
# Run as Administrator
net stop winnat
net start winnat
This flushes all NAT rules and rebuilds them. Restart Docker Desktop afterward, and UDP forwarding should come back.
But here’s the catch — this is a band-aid, not a fix. I’ve seen cases where winnat dies every two hours. You’ll be restarting it constantly.
Option 2: Force Rebuild Docker’s NAT Network
When WinNAT restart doesn’t cut it, the Docker default NAT network might be corrupted.
# 1. Stop all containers
docker stop $(docker ps -aq)
# 2. Delete Docker's default NAT network
docker network rm nat
# 3. Restart Docker Desktop (auto-rebuilds the nat network)
After this, Docker re-registers all port mappings with WinNAT. This usually fixes persistent UDP forwarding issues.
Option 3: Switch to Host Network Mode (Bypasses WinNAT)
If your container only needs UDP, use --network host to bind directly to the host’s network stack:
docker run --network host -d your-image
UDP traffic goes straight to the container process — WinNAT is completely out of the picture.
Warning: Host network mode on Windows has limitations. Some Docker Desktop versions don’t fully support it. You also lose port isolation — all container ports are exposed on the host.
Option 4: Explicit Protocol Binding in docker-compose
Sometimes the issue is Docker trying to bind both TCP and UDP to the same port, causing a conflict.
version: '3.8'
services:
snmptrapd:
image: your-snmp-image
ports:
- "162:162/udp" # UDP only
# Don't write "162:162" — that binds TCP AND UDP
Explicitly specifying /udp prevents Docker Desktop from creating conflicting port registration entries.
Option 5: The Nuclear Option — Migrate to WSL2 Native Docker
If nothing above works, you’re hitting a fundamental WinNAT bug that can’t be fixed from the user side.
The real solution: Switch Docker Desktop to the WSL2 backend, then run the Docker daemon natively inside WSL2’s Linux environment.
# 1. Enable WSL2 backend in Docker Desktop
# Settings → General → Use the WSL 2 based engine
# 2. Run Docker directly in WSL2
wsl --set-default-version 2
wsl
# Inside WSL2 terminal:
sudo dockerd &
docker run -p 5005:5005/udp your-image
Inside WSL2, Docker uses the native Linux network stack. UDP forwarding follows the Linux kernel’s behavior — stable, reliable, and free from WinNAT’s nonsense.
Comparison: Which Fix Should You Pick?
| Approach | Success Rate | Durability | Complexity | Impact on Other Containers |
|---|---|---|---|---|
| Restart WinNAT | 60% | Low (hours to days) | Low | Brief interruption |
| Rebuild NAT network | 80% | Medium (days to weeks) | Medium | All containers restart |
| Host network mode | 95% | High | Low | Loses port isolation |
| Explicit protocol in compose | 40% | Depends on root cause | Low | None |
| Migrate to WSL2 | 99% | Permanent | High (environment migration) | Full redeploy needed |
FAQ
Q: Why does UDP port forwarding fail more often than TCP on Windows?
A: Windows’ WinNAT component has solid stateful tracking for TCP sessions. But UDP is connectionless — WinNAT’s NAT session table handles UDP entries poorly, especially regarding timeout and automatic re-creation. When a UDP mapping entry gets corrupted or times out inside WinNAT, it doesn’t auto-recover, killing all subsequent UDP traffic to that port.
Q: How do I verify whether UDP forwarding is actually working?
A: Two-step verification. Step 1: Start a UDP listener inside the container (nc -ul 5005), then test from the host using Test-NetConnection -ComputerName 127.0.0.1 -Port 5005 -Protocol UDP in PowerShell. Step 2: Capture loopback traffic with Wireshark. If you see UDP packets but the container doesn’t receive them, the issue is at the WinNAT layer. If Wireshark shows nothing, the problem is deeper in the stack.
Q: How do I fix Docker Desktop’s “Ports are not available” error for UDP?
A: Run net stop winnat && net start winnat to clear the NAT state table, then restart Docker Desktop. If the error persists, check if another process (Hyper-V VM, Windows service) has claimed the UDP port. Use netstat -an | findstr :5005 to identify conflicts.
Q: Why doesn’t this happen on Linux?
A: Docker on Linux uses the kernel’s iptables/nftables directly for port forwarding — it’s a native capability of the Linux networking stack with mature UDP support. Docker Desktop on Windows has to go through two translation layers: Hyper-V virtualization and WinNAT. Each layer adds another potential failure point.
References & Community Insights
The following authoritative resources were referenced for architectural best practices and specifications: