Conversation
|
Download the artifacts for this pull request:
See Testing a PR. |
|
WSL2 manual test results (all passing):
|
574a89d to
d8a7581
Compare
There was a problem hiding this comment.
I tested in on Linux by running in one window:
docker run --rm -it -p 80:80 -p 443:443 nginx
In another window:
$ ddev utility port-diagnose
Port diagnostics for project: l12
Unable to identify the process without elevated privileges.
Running: sudo /usr/sbin/lsof -iTCP:80 -sTCP:LISTEN -n -P
You may be prompted for your password.
Port 80 (router HTTP): IN USE by docker-proxy (PID 667534, cmd=/usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.0.2 -container-port 80 -use-listen-fd) [Linux]
sudo kill 667534
Port 443 (router HTTPS): IN USE by docker-proxy (PID 667574, cmd=/usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 443 -container-ip 172.17.0.2 -container-port 443 -use-listen-fd) [Linux]
sudo kill 667574
Port 8025 (Mailpit HTTP): Available
Port 8026 (Mailpit HTTPS): Available
Port 8143 (XHGui HTTP): Available
Port 8142 (XHGui HTTPS): Available
$ sudo kill 667534
$ sudo kill 667574
$ ddev utility port-diagnose
Port diagnostics for project: l12
Port 80 (router HTTP): Available
Port 443 (router HTTPS): Available
Port 8025 (Mailpit HTTP): Available
Port 8026 (Mailpit HTTPS): Available
Port 8143 (XHGui HTTP): Available
Port 8142 (XHGui HTTPS): Available
All required ports are available.
$ ddev start
...
Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint ddev-router (1023cd8a4df8930a6dd04dc00acfc150b9a2c89df797a1b66857d6df36b6e161): Bind for 0.0.0.0:80 failed: port is already allocated'
Just saying that sudo kill isn't always going to work, because it didn't actually kill the docker run process, because it also created busy ports for IPv6:
$ sudo netstat -tulpn | grep LISTEN
tcp6 0 0 :::80 :::* LISTEN 667547/docker-proxy
tcp6 0 0 :::443 :::* LISTEN 667581/docker-proxy
macOS Manual Test ResultsTested with all 5 Docker providers running simultaneously on macOS arm64 (Apple M4): OrbStack, Colima, Lima, Rancher Desktop, Docker Desktop. Each scenario runs
How same-provider container identification worksFor same-provider conflicts (tests 2–6), the tool uses the Docker API ( Rancher Desktop noteRancher Desktop's ssh port-forward mux uses |
Linux Test MatrixTested on Ubuntu 25.10 (Linux arm64) with all three Docker providers running simultaneously: docker-ce (rootful), docker-rootless, podman-rootless. Each scenario runs
NotesProcess names by provider:
lsof absent (scenarios 4–5): Falls back to Podman container lookup fix: Podman's container-list API omits the IP field from port entries. The original New process names added to hints: |
Windows (native) Manual Testing — arm64 and amd64, Docker Desktop and Rancher DesktopTested on:
Bugs found and fixedBug: All ports falsely reported IN USE on Windows when nothing is listening (commit On Windows-native, Bug: Docker Desktop container reported twice per port (commit On Docker Desktop for Windows, a container binding port 80/443 caused both Test results
Note on W6 / cross-provider behavior: When both Rancher Desktop and Docker Desktop are installed and running simultaneously on Windows, Docker Desktop claims the Additional fixes made during Windows testing
Key things verified on Windows (amd64 + arm64)
Proposed additional test scenarios (Groups G/H)Group G — Rancher Desktop container conflict (WSL2, Rancher only): Group H — Different WSL2 distro holds port 80: # In PowerShell, start a listener in a second distro:
wsl -d Ubuntu-22.04 -- sudo service nginx start
# Then in ddev distro:
# ddev utility port-diagnose
# Expected: wslrelay [Windows] + "A WSL2 distro is forwarding this port to Windows.
# Otherwise check which distro holds it — in PowerShell:
# wsl --list
# wsl -d <distro> -- ss -tlnp"
wsl -d Ubuntu-22.04 -- sudo service nginx stop |
WSL2 Test ResultsEnvironment: WSL2 Ubuntu arm64, NAT networking mode, docker-ce (rootful), passwordless sudo, lsof installed unless noted. Tested with binary built from this branch. Bugs found and fixed during this session (commit 36c6f82):
Group A — Baseline
Group B — Linux-side conflicts (user-owned, visible without sudo)
Group C — Linux-side conflicts (root-owned, requires sudo)
Group D — Windows-side conflicts
Group E — Cross-distro (listener in another WSL2 distro)
Group F — Port config edge cases
|
|
This is crazy territory, with results being different on every OS and Docker provider. I didn't expect to spend all day on it. It's good enough I imagine, and involves no risk that I can see. |
tyler36
left a comment
There was a problem hiding this comment.
Test on WSL2 (Ubuntu 24.04) on Win10, with Docker Desktop and works as expected.
I get "wslrelay.exe [Windows]" messages when running denying sudo access and "[Linux (WSL2)]" messages when allowing sudo access . Both messages point to a WSL-side conflict so should not be a problem.
Test
WSL2 (Ubuntu 24.04) on Win10
$ ddev -v
ddev version v1.25.1-108-g98312a83c- Start Apache on port 80
$ sudo systemctl start apache2- Run diagnostic WITHOUT sudo use
$ ddev utility port-diagnose
...
Allow sudo use? [y/N] (no): n
Port 80 (router HTTP): IN USE by wslrelay (PID 21268, cmd=C:\Program Files\WSL\wslrelay.exe) [Windows]- Run diagnostic WITH
sudouse
$ ddev utility port-diagnose
...
Allow sudo use? [y/N] (no): y
Port 80 (router HTTP): IN USE by apache2 (PID 10171, cmd=/usr/sbin/apache2 -k start) [Linux (WSL2)]- Stop Apache2
sudo systemctl stop apache2 - Run diagnostic WITHOUT sudo use
$ ddev utility port-diagnose
...
Allow sudo use? [y/N] (no): n
Port 80 (router HTTP): Available- Run diagnostic WITH sudo use
$ ddev utility port-diagnose
...
Allow sudo use? [y/N] (no): y
Port 80 (router HTTP): Availableddev utility port-diagnose command to identify port conflictsddev utility port-diagnose command to identify port conflicts, fixes #8085
stasadev
left a comment
There was a problem hiding this comment.
Looks good to me.
Thank you for chasing down rootful/rootless path, and so many testing scenarios (which I didn't test).
I have a small nitpick about highlighting ddev utility port-diagnose in the docs.
| * IIS on Windows (can affect WSL2). You’ll have to disable it in the Windows settings. | ||
|
|
||
| To dig deeper, you can use a number of tools to find out what process is listening. | ||
| To dig deeper, run `ddev utility port-diagnose` from your project directory. It checks each port your project needs, identifies the blocking process by name and PID, and suggests how to stop it: |
There was a problem hiding this comment.
This is added too far down the page, people just won't read this info.
Considering the effort put into this command, I suggest moving it or adding a tip at the top of:
https://ddev--8260.org.readthedocs.build/en/8260/users/usage/troubleshooting/#web-server-ports-already-occupied
There was a problem hiding this comment.
Reworked the troubleshooting.md, need to re-read it in the morning, but it should be better and this should be ready to go.
…icts Implements issue ddev#8085: a new `ddev utility port-diagnose` command (aliased as `ddev ut port-diagnose`) that identifies which processes are occupying ports needed by DDEV projects. Features: - Checks project-specific ports (router HTTP/HTTPS, Mailpit, XHGui) - Falls back to checking ports 80 and 443 when outside a project - Identifies blocking processes by PID, name, and full command line - On Linux/macOS: uses lsof with fallback to ss - On Windows: uses PowerShell Get-NetTCPConnection - On WSL2: checks both Linux and Windows sides independently - Provides actionable hints for kill/stop/disable/uninstall based on process Tests: - TestPortDiagnoseAvailablePort: smoke test - TestPortDiagnoseInUsePort: verifies process identification - TestPortHints: validates hint generation for known processes Docs: - Updated troubleshooting.md with port-diagnose example and guidance - Added before manual lsof/netstat instructions Implementation follows DDEV patterns: - Uses existing netutil.IsPortActive() for initial checks - Reuses platform detection from nodeps package - Follows DDEV diagnostic command structure - Cleans golangci-lint with SplitSeq and CutPrefix modernization
…ter process detection - Require ddev poweroff before running, to avoid false positives from running projects - Terse one-line output per port: "Port 80 (HTTP): IN USE by nginx (PID 1234) [Linux]" - Better process detection chain: lsof -> sudo lsof -> ss -> /proc/net/tcp - No longer depends on lsof being installed; falls through to ss and /proc/net/tcp - Still doesn't detect nc on Linux (process detection issue to investigate) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…skip ci] - Check for ddev-router container in addition to running projects - Fix TestPortHints to work on macOS (no systemctl) and all platforms Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…p ci] - Remove linux-only guard so sudo lsof runs on macOS too - Drop -n flag and connect stdin/stderr so sudo can prompt for password - Print one-time message explaining why elevated privileges are needed Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…se hints [skip ci] - Use /usr/sbin/lsof with fallback to PATH lsof (macOS keeps it in /usr/sbin) - Remove incorrect "brew uninstall httpd" hint for apache on macOS - Make all hints concise single-line output Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…p ci] Apache2 (and similar) runs parent + worker processes all listening on the same port. Deduplicate by process name so only one entry is shown per port. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…nection listeners [skip ci] IsPortActive() dials the port, which causes single-connection listeners like `nc -l` to accept and exit before lsof can find them. Restructured to find processes first (lsof/ss/proc), then fall back to IsPortActive only when no processes are found. Also improved the "unidentifiable" message to suggest the manual sudo lsof command, and suppressed noisy sudo stderr when password is required. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…kip ci] - Separate nc-based tests for Linux, WSL2, macOS (skip if nc unavailable) - Direct tests for each detection method: lsof, ss, /proc/net/tcp, Windows - TestDeduplicateByName for apache2 worker process dedup - Expanded TestPortHints with subtests for all known processes - TestPortHintsPlatformSpecific verifies correct commands per OS Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…p ci] On macOS, lsof -sTCP:LISTEN was still returning ESTABLISHED connections (Chrome, Discord outbound to remote port 443). Now parseLsofOutput requests the T field (-F pcnT) and only accepts entries with TST=LISTEN. Also shows the full sudo command being run so users know what to expect and can run it themselves. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…skip ci] Some macOS lsof versions may not emit the T (TCP state) field. Now entries without a TST= line are accepted (trusting the -sTCP:LISTEN filter), while entries with an explicit non-LISTEN state are rejected. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
macOS nc does not allow -p with -l (port must be positional); Linux nc requires -p to specify the listen port. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…kip ci] $pid is a read-only automatic variable in PowerShell. Renamed to $procId in the Get-NetTCPConnection script — this was broken on all Windows/WSL2. Added TestFindWindowsPortProcesses that starts a .NET TcpListener on the Windows side via PowerShell and verifies findWindowsPortProcesses finds it. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…hints - Update troubleshooting docs to match current terse output format - Reset sudoMessageShown at start of runPortDiagnose to avoid state leaks - Early break in findPortProcessesProcNet after matching a PID's inode - Validate port is numeric before interpolating into PowerShell script - Show PowerShell hint instead of sudo lsof on Windows when process is unidentifiable Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Three issues fixed: 1. `lsof -sTCP:LISTEN` on macOS arm64 returns UDP connections (e.g. Chrome QUIC traffic to remote port 443), causing false port-conflict reports. Fixed by using `-iTCP:<port>` instead of `-i :<port>` to restrict lsof to TCP sockets before any filtering. 2. `findPortProcessesSudoLsof` connected stdin unconditionally, so in CI environments where Buildkite provides a pseudo-terminal, sudo would prompt for a password and hang for hours. Fixed with an `isTerminal` check — stdin is only connected in interactive sessions. 3. The "Unable to identify the process without elevated privileges" message and sudo attempt fired even for free ports (lsof exits 1 when nothing matches). Moved sudo detection out of `findPortProcesses` into `runPortDiagnose`, gated behind `IsPortActive`, so the elevated path only runs when something is actually listening. Also fixes `TestFindPortProcessesNC_macOS` to call `findPortProcessesLsof` directly (skipping the sudo fallback) and skip gracefully when non-sudo lsof cannot see nc — the macOS CI restriction that triggered the hang. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Add docker-proxy case: killing the proxy doesn't free the port; guide users to 'docker ps' / 'docker stop <name>' instead. - Extend OrbStack match to cover "OrbStack Helper" (the process name lsof reports on macOS/OrbStack), with the same docker stop guidance. - Replace 'sudo kill <pid>' default with a softer suggestion: "Consider stopping this process using OS tools, e.g. 'kill <pid>'". - Replace IsPortActive with isPortBindable (net.Listen on 0.0.0.0) for the sudo-gate availability check. IsPortActive dials the Docker IP which on OrbStack is a VM gateway address, not loopback, so it missed listeners on *:80/443. A bind attempt correctly detects any listener on any local interface. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
When a port is held by a Docker provider process (docker-proxy, ssh/limactl for Colima, OrbStack Helper), use context-aware hints: - If the process belongs to the active Docker provider, query the Docker API (GetDockerContainers) to find exactly which container holds the port and emit: "Container 'name' is holding this port. Run: docker stop name" - If the process belongs to a different (non-active) Docker provider, suggest stopping that provider instead (colima stop, quit OrbStack menu, etc.) Add helpers: - activeDockerProvider(): uses dockerutil.IsColima/IsOrbStack/etc. to name the currently active provider - findContainerForPort(): queries the Docker API by PublicPort to find the container name without shelling out to docker ps - dockerContainerHints(): wraps findContainerForPort with a fallback - dockerProviderHints(): dispatches to container hints or provider-stop hints Also add Colima/Lima ssh-mux and limactl cases to portHints, and thread cmdLine and port through the portHints signature to support these lookups. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…aths [skip ci] - Detect plain Lima (/.lima/ in cmdline) separately from Colima (/.colima/) so limactl/ssh port-forwards on Lima without Colima are correctly identified - Add Lima and Rancher Desktop to dockerProviderHints non-active provider branch - Remove hardcoded /Users/testbot paths from hint tests; use relative ~/.lima and ~/.colima paths (expanded at test time) so CI runners with any home directory pass correctly - Relax provider hint test assertions to check for "port" (present in all provider branches) rather than provider-specific strings that vary depending on which Docker provider is active in the test environment Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…kip ci] Two fixes discovered during manual testing with all 5 macOS Docker providers running simultaneously: 1. Rancher Desktop forwards ports via an ssh mux whose cmdline contains "rancher-desktop/lima" — add a matching case before the generic /.lima/ case so it maps to "Rancher Desktop" rather than "Lima", and add the Rancher Desktop entry to dockerProviderHints. 2. Rancher Desktop's ssh mux uses SO_REUSEPORT, which allows a second net.Listen to succeed on the same port even when a listener is already active. This caused isPortBindable to return true (free) for a port actually held by a Rancher Desktop container. Replace isPortBindable with isPortFree, which performs a bind followed by a 250ms dial to 127.0.0.1:<port>: if something answers the dial after a successful bind, the port is in use despite the bind succeeding. All 13 manual test scenarios passed after these fixes (see PR comment). Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
When lsof is absent (stock Ubuntu), fall back to sudo ss -tlnp to find root-owned listeners such as docker-proxy. Also update the "unable to identify" hint to suggest sudo apt-get install lsof when lsof is missing. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…Linux [skip ci]
Adds full Linux multi-provider support to `ddev utility port-diagnose`:
- `activeDockerProvider()` now returns "Podman", "Docker (rootless)", or
"Docker" for Linux providers (previously returned "" for all of them).
- `portHints()` recognises the Linux rootless port-forwarding processes:
`rootlesskit`/`rootlessk` (Docker rootless) and `rootlessport`/`rootlessp`
(Podman). Both now route through `dockerProviderHints()` for cross-provider
awareness. `docker-proxy` likewise routes through `dockerProviderHints("Docker")`
instead of calling `dockerContainerHints` directly.
- `dockerProviderHints()` now emits provider-specific stop hints for each
Linux provider when it is not the active one, e.g. "Podman has a container
holding this port (but is not your active Docker provider). Check: podman ps".
- `findContainerForPort()` drops the `p.IP.IsValid()` guard: Podman's
container-list API omits the IP field entirely, so the guard prevented
container lookup from ever succeeding under Podman. Checking `PublicPort`
alone is correct — a zero PublicPort won't match a real port number.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Bugs found and fixed during WSL2 (NAT mode, docker-ce) manual testing: 1. EACCES false positive on ports < 1024: isPortFree treated EACCES (permission denied) the same as EADDRINUSE (address in use). On Linux with ip_unprivileged_port_start=1024, binding port 80 as non-root returns EACCES — port is free but was reported as IN USE. Fix: distinguish EACCES from other bind errors and fall through to a dial-only check. 2. Root-owned Linux listener hidden by wslrelay: When a root process held a port, findPortProcesses (unprivileged) returned empty. But findWindowsPortProcesses found wslrelay.exe, making allProcs non-empty and skipping the sudo lsof escalation. Fix: track Linux-side and Windows-side results separately; sudo escalation is driven by whether the Linux side found something, independent of the Windows side. 3. Stale compose YAML overriding current project port config: GetPrimaryRouterHTTPPort reads DDEV_ROUTER_HTTP_PORT from the last rendered compose YAML (set during previous ddev start), overriding the current project config. Fix: set app.ComposeYaml = nil before reading ports, same as Start() already does. 4. Spurious "Unable to identify..." and "Running: sudo lsof..." when port is held only on the Windows side: when a Windows-only process held a port (e.g. PowerShell TcpListener), isPortFree returned true for the Linux side but the code still entered the sudo escalation path because allProcs was non-empty from the Windows check. Fix: call isPortFree once and use it to gate sudo escalation — only escalate when the port is confirmed in use on the Linux side. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…istro conflicts [skip ci] The wslrelay hint now calls findContainerForPort to distinguish between a Docker/Rancher Desktop container forwarded via WSL2 and a service running in a different WSL2 distro. When a container is found, it is named with a docker stop suggestion; otherwise the user is guided to check each distro with wsl --list and ss -tlnp. Also fix docker-proxy being misidentified as Docker CE (rootful) when Rancher Desktop is the active provider — Rancher Desktop in dockerd mode uses docker-proxy internally, so it should route through dockerContainerHints instead of the cross-provider warning. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…ip ci] On Windows-native, findWindowsPortProcesses returning empty was treated as "IN USE (unable to identify)" because the isPortFree check was gated behind !nodeps.IsWindows(). This produced false positives for every free port. Add an explicit isPortFree check on Windows before the "unable to identify" path so that genuinely free ports report Available. Found during Windows native manual testing (test matrix scenario #1). Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…resent On Docker Desktop for Windows, a container binding port 80/443 causes both wslrelay.exe (the WSL2→Windows relay) and com.docker.backend.exe (the Docker Desktop proxy) to appear as separate port-holders. The old code reported both, producing two identical "IN USE" lines per port pointing to the same container with the same docker-stop hint. Add suppressWSLRelayIfRedundant(), called after deduplicateByName(), which drops wslrelay when any other process is also present. wslrelay is always a subordinate relay; the co-listed provider process carries the actionable hint. When wslrelay is the sole entry (bare WSL2 service case) it is preserved unchanged. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Extracts the container-matching loop from findContainerForPort into a pure helper containerNameForPort(hostPort int, containers []container.Summary) so it can be tested without a live Docker API. TestContainerNameForPort covers: - Docker CE style (IP field populated with 0.0.0.0) - Podman style (IP field absent / zero netip.Addr) - Port not matched - Unexposed port (PublicPort == 0) does not false-match - Leading slash stripped from container name Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…sudo flag Before running any elevated command, show the exact full-path sudo command(s) that may be used and ask via util.ConfirmTo. Each invocation also prints the command being run. --allow-sudo skips the prompt (useful for scripts/CI). Document the flag in commands.md and troubleshooting.md. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Use exec.LookPath as fallback so the lsof path passed to sudo is always absolute, not a bare command name that could be hijacked via PATH. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Resolving sudo/lsof/ss via PATH is a security risk; accept only known canonical locations (/usr/bin/sudo, /usr/sbin/lsof, /usr/sbin/ss, etc.). If a tool is not found at a canonical path it is treated as unavailable. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Set global router ports to unprivileged test ports so runPortDiagnose can be exercised end-to-end without root. Tests skip when DDEV is active. Also read router ports from globalconfig when not in a project directory. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…tests GetActiveProjects() misses a running router holding ports. PowerOff() stops both all projects and the router, ensuring a clean slate for the test. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
When Docker Desktop is the active provider, the hint describes the container (not the provider name), so assert "port" which is present in all branches — consistent with other provider-dependent test cases. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
The manual lsof/netstat/ps/Windows-netstat diagnostic blocks are now redundant since `ddev utility port-diagnose` identifies the blocking process automatically. Merge the duplicate Methods 1 and 3, reorder the common-tools list under Method 1 where it belongs, and update the WSL2 section to point at port-diagnose instead of removed commands. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
98312a8 to
c7debd2
Compare
|
I read https://ddev--8260.org.readthedocs.build/en/8260/users/usage/troubleshooting/#web-server-ports-already-occupied again, thanks for your suggestions. It's trimmed down, with the new tool as the lead. This whole page is a bit too much, trying to cover everything ever discovered in the history of DDEV :) I'm not sure what can be done about that. |
ddev utility port-diagnose command to identify port conflicts, fixes #8085ddev utility port-diagnose command to identify port conflicts, fixes #8085 (#8260) [skip ci]
The Issue
ddev utility port-diagnose#8085DDEV users struggle to identify which processes are occupying the ports DDEV needs (80, 443, or project-specific ones), especially on Windows/WSL2 where the blocking process may be on either side.
How This PR Solves The Issue
Adds
ddev utility port-diagnosethat:lsof→sudo lsof(with password prompt) →ss→/proc/net/tcp(Linux)lsof→sudo lsof(macOS)Get-NetTCPConnection(Windows, WSL2 Windows-side)nc -lManual Testing Instructions
Prerequisites for all platforms: Run
ddev powerofffirst. The command will refuse to run if DDEV services are active.macOS
sudo apachectl startIN USE by httpd (PID ...) [macOS]with hintsudo apachectl stop. You will be prompted for sudo password if lsof can't see the process without it. Port 443 and others should showAvailable.sudo apachectl stopnc -l -k 8142 &IN USE by nc. Kill nc afterward.cd /tmp && ddev utility port-diagnose) — should check only ports 80 and 443.Linux (non-WSL2)
sudo systemctl start apache2(ornginx)IN USE by apache2 (PID ...) [Linux]with systemctl stop/disable hints and apt-get remove hint.sudo systemctl stop apache2nc -l -k -p 8142 &IN USE by nc.sudo apt remove lsof, re-run — should fall through tossor/proc/net/tcpdetection and still find nc.WSL2
sudo systemctl start apache2IN USE by apache2 (PID ...) [Linux (WSL2)].$l = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Any, 8142); $l.Start(). Run port-diagnose again — should show the Windows process with[Windows]label.wslrelayon the Windows side, the hint should say to check WSL2 and runddev poweroffthere.nc -l -k -p 8142 &— should be detected.Windows (native, Docker Desktop with WSL2 backend)
IN USE by powershell (PID ...) [Windows]withStop-Processhint.Available.All platforms: Edge cases
ddev poweroff.All required ports are available.Automated Testing Overview
Platform-specific tests with real process simulation:
TestFindPortProcessesNC_Linux,_WSL2,_macOS— start nc, verify detection (skip if nc unavailable)TestFindPortProcessesOwnProcess(Unix),_Windows— Go test holds a port, verifies PID matchTestFindPortProcessesLsof,TestFindPortProcessesSS,TestFindPortProcessesProcNet— test each detection method directlyTestFindWindowsPortProcesses— starts a .NET TcpListener via PowerShell, verifies detectionTestParseLsofOutputFiltersListen(ESTABLISHED connections filtered),TestParseLsofOutputNoStateField(macOS compatibility)TestDeduplicateByName,TestPortHints(all known processes),TestPortHintsPlatformSpecificRelease/Deployment Notes