docs: add Coder Desktop stale-tunnel recovery and improve macOS log capture (#26735)

## What

Adds a **Recovering from a stale tunnel** section to the Coder Desktop
user guide, with separate macOS and Windows procedures, and tightens the
existing macOS log-collection instructions.

## Why

Users in the field have hit a state where Coder Desktop's menu bar /
tray shows **Coder Connect** as enabled but the embedded tunnel is no
longer working:

* `workspace.coder` fails to resolve (`No such host`), or
* DNS returns stale `fd60:627a:a42b::/48` addresses that no longer
route, causing `coder ssh`, file sync, and the directory picker to hang.

Related issues:

* coder/coder#26669 — `ExistsViaCoderConnect` false positives when Coder
Desktop has stale DNS
* coder/coder-desktop-windows#171 — Tray reports Coder Connect as
healthy while tunnel/DNS is broken

Until the underlying state-management gap is fixed in the apps, the docs
should give users (and support) a safe, repeatable way to recover
without rebooting.

## Changes

`docs/user-guides/desktop/index.md`:

1. **New "Recovering from a stale tunnel" section** under
Troubleshooting:
* **macOS:** stop the VPN configuration with `scutil --nc stop`, quit
the app via `osascript`, restart the helper daemon in place with
`launchctl kickstart -k system/com.coder.Coder-Desktop.Helper`, flush
DNS caches, then relaunch.
* Includes a warning to **not** use `launchctl bootout`, which removes
the daemon from launchd's system domain entirely and is not
re-bootstrapped on app relaunch.
* Includes a verification step using the built-in sentinel hostname
`is.coder--connect--enabled--right--now.coder` (defined in
`tailnet/conn.go` as `IsCoderConnectEnabledFmtString`) so users don't
need a workspace name to confirm the tunnel is healthy.
* Uses `dig @fd60:627a:a42b::53` (explicit server) and `dscacheutil -q
host -a name` because plain `dig` does not respect the macOS system
resolver.
* **Windows:** stop the app and `Coder Desktop` service, flush DNS,
restart, then verify the NRPT rule and Wintun adapter. Notes that
`ipconfig /flushdns` does not reset the embedded resolver and that
filtering agents (e.g., Zscaler) may still shadow `.coder` lookups.
2. **macOS log-collection improvements:**
* Switch the predicate from `subsystem == "com.coder.Coder-Desktop"` to
`subsystem BEGINSWITH "com.coder.Coder-Desktop"` so the export captures
the app, helper daemon, and network extension (which all log under
prefixed subsystems).
* Add a `log stream` example for live tailing while reproducing an
issue.

## Verification

* `npx markdownlint-cli2 docs/user-guides/desktop/index.md` — 0 errors.
* macOS recovery steps were validated end-to-end on a real install (the
`kickstart -k` form, in particular, was confirmed to restart the helper
without breaking the install, unlike `bootout`).

---

Created on behalf of @mdanter

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: Atif Ali <atif@coder.com>
Co-authored-by: Nick Vigilante <nickvigilante@users.noreply.github.com>
Co-authored-by: Matyas Danter <mdanter@gmail.com>
This commit is contained in:
blinkagent[bot]
2026-08-14 12:31:04 -04:00
committed by GitHub
co-authored by blink-so[bot] Atif Ali Nick Vigilante Matyas Danter
parent 57dc47dc42
commit 043bebb7bc
+130 -3
View File
@@ -169,7 +169,121 @@ You can also configure a `Updater:ForcedChannel` string value to lock users to a
- Check system permissions for network extensions - Check system permissions for network extensions
- Ensure only one copy of Coder Desktop is installed - Ensure only one copy of Coder Desktop is installed
### Collecting Logs ### Recover from a stale tunnel
If the menu bar or tray shows **Coder Connect** as enabled but workspaces are
unreachable (SSH hangs, `workspace.coder` fails to resolve, or file sync cannot
list the workspace directory), the embedded tunnel may be in a stale state.
Restart the helper components first.
If that doesn't resolve the issue, reboot your computer.
<div class="tabs">
#### macOS
Run the following commands in a terminal.
These steps leave the app and helper daemon installed.
They only restart the running tunnel and flush DNS caches.
1. Stop the Coder VPN configuration:
```shell
vpn_name=$(scutil --nc list | grep "com.coder.Coder-Desktop" | awk -F'"' '{print $2}' | tail -n1)
if [ -n "$vpn_name" ]; then scutil --nc stop "$vpn_name"; fi
```
2. Quit the Coder Desktop app:
```shell
osascript -e 'tell application id "com.coder.Coder-Desktop" to quit'
```
3. Restart the helper daemon in place:
```shell
sudo launchctl kickstart -k system/com.coder.Coder-Desktop.Helper
```
> [!WARNING]
> Do not use `launchctl bootout` here. `bootout` removes the daemon from
> launchd's system domain entirely, and relaunching the app does not
> re-bootstrap it. Use `kickstart -k` to restart it in place.
4. Flush DNS caches:
```shell
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder
```
5. Confirm the stale tunnel is gone (the command should print nothing):
```shell
scutil --nc list | grep "com.coder.Coder-Desktop"
```
6. Relaunch **Coder Desktop** from your `/Applications` folder and toggle **Coder Connect** back on.
To verify the tunnel is healthy after relaunching, query the built-in sentinel
hostname against Coder Desktop's embedded DNS server:
```shell
dig @fd60:627a:a42b::53 AAAA is.coder--connect--enabled--right--now.coder +short
dscacheutil -q host -a name is.coder--connect--enabled--right--now.coder
```
Both commands should return an `fd60:627a:a42b::/48` address.
If `dig` returns nothing or `dscacheutil` reports no entries, Coder Connect is not publishing DNS.
[Collect logs](#collect-logs) and file an issue.
#### Windows
Run the following in an **elevated PowerShell** session.
The service restart clears the embedded DNS state.
The NRPT and adapter checks confirm the system routing policy is intact.
1. Stop the app and the VPN service:
```ps1
Get-Process -Name "Coder Desktop" -ErrorAction SilentlyContinue | Stop-Process -Force
Stop-Service -Name "Coder Desktop" -Force
```
2. Flush DNS caches:
```ps1
ipconfig /flushdns
Clear-DnsClientCache
```
3. Start the service again and relaunch the app:
```ps1
Start-Service -Name "Coder Desktop"
Start-Process "C:\Program Files\Coder Desktop\CoderDesktop.exe"
```
4. After re-enabling Coder Connect in the tray, verify the NRPT rule and the Wintun adapter exist:
```ps1
Get-DnsClientNrptRule | Where-Object { $_.Namespace -like "*.coder" }
Get-NetAdapter | Where-Object { $_.InterfaceDescription -like "*Wintun*" }
```
If either command returns nothing while Coder Connect shows as enabled,
the tunnel did not finish coming up. Disable and re-enable the toggle, or
repeat the steps above.
> [!NOTE]
> `ipconfig /flushdns` does not reset Coder Desktop's embedded DNS resolver;
> restarting the service is what clears its internal cache. If you are running
> a network filtering agent (for example, Zscaler), it may shadow `.coder`
> lookups even after the tunnel comes back. Check with your IT team if DNS
> still fails after a clean restart.
</div>
### Collect logs
When reporting an issue, attach the relevant log files so we can diagnose it faster. When reporting an issue, attach the relevant log files so we can diagnose it faster.
@@ -182,11 +296,24 @@ Coder Desktop and its network extension write to the Apple [unified logging syst
1. Export the unified logs for the last hour with the `log` command: 1. Export the unified logs for the last hour with the `log` command:
```sh ```sh
log show --predicate 'subsystem == "com.coder.Coder-Desktop"' \ log show --predicate 'subsystem BEGINSWITH "com.coder.Coder-Desktop"' \
--info --debug --last 1h > ~/Desktop/coder-desktop.log --info --debug --last 1h > ~/Desktop/coder-desktop.log
``` ```
Adjust `--last` (e.g. `30m`, `2h`, `1d`) to cover the time the issue occurred. You can also view the same logs interactively in **Console.app** by filtering on the `com.coder.Coder-Desktop` subsystem. The `BEGINSWITH` predicate captures the app, the helper daemon, and the
network extension, which all log under subsystems prefixed with
`com.coder.Coder-Desktop`. Adjust `--last` (e.g. `30m`, `2h`, `1d`) to
cover the time the issue occurred.
You can stream the logs live while reproducing an issue:
```shell
log stream --predicate 'subsystem BEGINSWITH "com.coder.Coder-Desktop"' --info --debug
```
You can also view the same logs interactively in **Console.app** by
filtering on `subsystem:com.coder.Coder-Desktop` with Info and Debug
messages enabled.
2. If you're using file sync, also collect the Mutagen daemon log: 2. If you're using file sync, also collect the Mutagen daemon log: