Repository navigation
fix(router): detect Docker Desktop connection resets on router ports after alias updates - #9026
Conversation
|
Download this PR's build for your OS and architecture with: ddev utility download-ddev --pr 9026Or download the artifact for your OS and architecture directly:
See Testing a PR. |
tyler36
left a comment
There was a problem hiding this comment.
I don't have access to a Mac so I can't confirm this resolves the issue.
However, I did spin up multiple PHP sites and a Laravel 13 site without issue.
Tested on WSL3 (Ubuntu 24.04.5) Win10
stasadev
left a comment
There was a problem hiding this comment.
One thing to fix in the test and a small one, both inline.
Claude Code ran two ddev starts with a new hostname on Docker 29 on Linux. The alias update path ran the new check without a warning, and every router port, 443 included, answered the plain HTTP probe. TestIsPortUnreachable fails at the reset case with the old dial-only check.
🤖 Reviewed with assistance from Claude Code
| t.Logf("%s failed at first (err=%v) and recovered; a known Docker Desktop port-forwarding delay", u, err) | ||
| continue | ||
| } | ||
| testcommon.AssertLocalHTTPContent(t, u+"/testfile.html", want, |
There was a problem hiding this comment.
This IsDockerDesktop() gate doesn't do anything. On any other provider, once logRouterDiagnostics returns recovered, the code falls through to AssertLocalHTTPContent, which makes one more request after the URL already recovered, so it passes. A URL that fails and recovers within 60s now passes everywhere, not only on Docker Desktop. Maybe fail on the first error instead:
t.Errorf("every reported URL should serve the project: %s err=%v body=%.200s", u, err, body)| t.Logf("router diagnostics: last router log lines (err=%v):\n%s", err, logs) | ||
| ports, err := exec.RunHostCommand("docker", "port", router.ID) | ||
| t.Logf("router diagnostics: router published ports (err=%v):\n%s", err, ports) | ||
| for _, netMode := range []string{dockerutil.NetName, "host"} { |
There was a problem hiding this comment.
Small thing: this curl from ddev_default always fails for the direct http://127.0.0.1:<port> URL, since 127.0.0.1 is the curl container itself there. Maybe skip that network for loopback URLs so the log doesn't mislead.
…after alias updates ## Short Summary (TL;DR) After DDEV reconnects `ddev-router` to update its aliases, Docker Desktop on macOS can forward a router port to a stale target, so requests get `connection reset by peer`. DDEV now warns about it, and `TestGetWebContainerDirectURLsWithGenericWebserver` logs better diagnostics and tolerates the delay on Docker Desktop. ## The Issue `TestGetWebContainerDirectURLsWithGenericWebserver` failed 15 times on `ddev-macos-arm64-mutagen` since 2026-10-08, counting retried jobs. That is about 7% of test jobs. Every failure came right after `Updating router network aliases in place`. Traefik had the route, but `127.0.0.1:443` or `:80` reset the connection and was still resetting 5s later. The reset is a long-standing Docker Desktop problem. ddev#8627 skipped about 18 tests for it. This test started requesting router URLs in ddev#8903, after that skip. `warnUnreachableRouterPorts` could not see it: it only looked for refused connections, and it only ran when the router was recreated. ## How This PR Solves The Issue - `netutil.IsPortUnreachable` (formerly `IsPortRefused`) sends a minimal HTTP request. A refused connection, or one that closes or resets before any response, counts as unreachable. - `warnUnreachableRouterPorts` also runs after the in-place alias update. - `logRouterDiagnostics` logs the router's published ports and curls the URL from containers on `ddev_default` and the host network. It then polls the URL for up to 60s and logs how long recovery takes. - On Docker Desktop, the test passes when the URL recovers within that time. Otherwise it fails as before. ## Manual Testing Instructions ```bash ddev utility download-ddev --pr 9026 export PATH="$HOME/tmp/ddev-download-ddev/pr-9026:$PATH" ddev start # in a first project ddev start # in a second project with a new hostname # Expect no "nothing answers on host port(s)" warning on a healthy provider ``` ## Automated Testing Overview `TestIsPortUnreachable` covers answering, refused, reset, and closed-without-answer listeners. In the Docker Desktop CI logs, look for `recovered after Ns` from `logRouterDiagnostics`. It shows whether polling is enough here and in `TestCmdLogs`, or whether these tests need the ddev#8627 skip. 🤖 Developed with assistance from [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Linux, a reset that arrives right after the handshake can fail the dial with `connect: connection reset by peer`. `IsPortUnreachable` only counted a refused dial, so it reported the port as reachable and `TestIsPortUnreachable` failed on the Linux GitHub runners. A write after a reset can also fail with `EPIPE`, so that now counts as dropped too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p the loopback curl on ddev_default Once logRouterDiagnostics reported recovery, the test fell through to AssertLocalHTTPContent, which passed because the URL now worked, so a failing-then-recovering URL passed on every provider. Fail on the first error instead, unless the URL recovered on Docker Desktop. A curl from ddev_default to 127.0.0.1 reaches the curl container itself, so skip that network for loopback URLs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2114cda to
a736517
Compare
|
Suggested changes done. I had Claude attempt to recreate the problem we see in tests on macOS DD, it wasn't able to. The only real risk in this IMO is catering too much to DD, and it's probably not doing too much. |
Short Summary (TL;DR)
After DDEV reconnects
ddev-routerto update its network aliases, Docker Desktop on macOS can keep forwarding a router port to a stale target, so requests getconnection reset by peer. DDEV now warns about it, andTestGetWebContainerDirectURLsWithGenericWebserverlogs better diagnostics and tolerates the delay on Docker Desktop.The Issue
Docker Desktop on macOS has reset connections to
ddev-routerports 80/443 for years. It happens after the router's network endpoint changes, either because DDEV recreates the router or because it disconnects and reconnects it toddev_default.History:
ddev-macos-arm64-mutagen, for exampleTestTraefikSimple,TestDdevStartMultipleHostnames, andTestHardenedStart.ddev_defaultand reconnects it, instead of recreating it.TestGetWebContainerDirectURLsWithGenericWebserverto request every router URL. It did not get the test(macos-docker-desktop): skip tests hitting a Docker Desktop connection reset #8627 skip.warnUnreachableRouterPorts. It only finds refused connections, and it runs only when the router is recreated. Docker Desktop accepts the connection and then resets it, so this check misses it.Since 2026-10-08,
TestGetWebContainerDirectURLsWithGenericWebserverfailed 15 times onddev-macos-arm64-mutagen, counting retried jobs. That is about 7% of test jobs on both agents, and it also failed onmain. Every failure came right afterUpdating router network aliases in place. Traefik had the route loaded, and the router log showed no request. Port 443 or 80 still reset the connection 5s later, while the other port and the direct web container port worked.TestCmdLogsshows the same reset.Examples:
main: HTTPS resetsHow This PR Solves The Issue
netutil.IsPortUnreachable(formerlyIsPortRefused) sends a minimal HTTP request. A refused connection, or one that closes or resets before any response, counts as unreachable.warnUnreachableRouterPortsalso runs after the in-place alias update.logRouterDiagnosticslogs the router's published ports. It curls the URL from a container onddev_default, which reaches the router directly. It also curls from a container on the host network, which skips Docker Desktop's host-side forwarding. Then it polls the URL for up to 60s and logs how long recovery takes.This PR does not fix Docker Desktop. It makes the problem visible to users and gives us the recovery time we need to decide between polling and skipping.
Manual Testing Instructions
Expect no
nothing answers on host port(s)warning on a healthy Docker provider.Automated Testing Overview
TestIsPortUnreachablecovers answering, refused, reset, and closed-without-answer listeners. In the Docker Desktop CI logs, look forrecovered after NsfromlogRouterDiagnostics. It shows whether polling is enough here and inTestCmdLogs, or whether these tests need the #8627 skip.🤖 Developed with assistance from Claude Code