Why dashboards fail while proxies keep tunneling
You finish installing Mihomo-compatible Clash, subscriptions resolve, Chromium loads foreign sites happily, then paste Yacd or Metacube at http://127.0.0.1:9090 only to stare at connection refused. Because SOCKS or HTTP mixed ports simultaneously work, adrenaline convinces novices the proxy stack cratered—even though only REST listeners misaligned.
This article concentrates on external-controller binds, Bearer secrets regenerated by wrappers, Defender-style firewall prompts isolating SOCKS but not dashboards, systemd races, duplicated profiles, LAN versus loopback semantics, and repeatable curl proofs. It purposely diverges from catch-all troubleshooting threads so checklist readers escape scroll fatigue yet can still escalate once REST probes stabilize—start wider diagnosis with our general Clash troubleshooting guide only after narrowing API listeners deliberately.
What external-controller actually powers
Mihomo-class cores advertise version payloads, proxies, selectors, tracing logs, rule snapshots, traffic counters, delayed tests, sniff toggles, and reload hooks beneath an HTTP-compatible REST façade. Lightweight skins—including Yacd, Metacube, or self-hosted Mesh boards—consume those JSON payloads; none fabricate sockets you suppressed.
Older forum posts mention bind-address for SOCKS snippets; mentally translate jargon to whichever schema field listens for REST tonight. Divergent vocabulary explains why rookies stare at impeccable YAML excerpts without realizing merged GUI layers overwrote identical lines microseconds later.
external-controller: 127.0.0.1:9090
Loopback binds versus purposeful LAN listens
Binding 127.0.0.1:9090 accepts handshakes from loopback callers only—even if another workstation shares cafeteria Wi-Fi or Ethernet VLANs identical to yours. Phones, televisions, roommate laptops slam into TCP refusal precisely because binds never promised those NIC identities.
Expanding binds to something like 0.0.0.0 publishes listeners on each IPv4 interface you expect, unlocking http://desktop-ip:9090 from trusted neighbors after restarts—but also exposing entire configurations to VLAN peers unless firewall ingress policies or VPN backhauls restrict scope mirrors advice we cite for SOCKS or mixed exposures on LAN-heavy guides such as LAN proxy ergonomics with Clash on Windows or macOS.
IPv6 homes might require :::9090 or bracketed literals; systemd templates occasionally regenerate IPv4-only excerpts after package refreshes.
# Loopback strictly
external-controller: 127.0.0.1:9090
# Deliberately LAN-visible (audit firewall first)
external-controller: 0.0.0.0:9090
Hands-on probes before blaming dashboards
Treat curl as your truth serum before flashy WebGPU dashboards tempt you:
- Locate the YAML the running daemon merges—not orphaned drafts lurking in Downloads after subscription imports.
- List sockets (
ss -tlnp | grep 9090on Linux shells, filterednetstat/powershell Get-NetTCPConnectionon Windows). Silence means refusal stems from binds, crashes, or port drift. - Curl
http://127.0.0.1:<port>/version; JSON containingmetafollows the spirit of the sanity check spelled out inside our Clash Meta upgrade guide version probe. - Widen binds only deliberately, rerun curls from auxiliary hosts inside firewall allowances you newly grant.
- journalctl/Event Viewer deltas highlight races when GUIs bounce daemons quicker than overlays flush.
- Note TLS versus plain HTTP distinctions—secured controllers handshake differently yet still hinge on binds.
Readers installing plain systemd units profit from aligning these probes with sequences inside our Mihomo systemd install article—service paths differ from flashy desktop exporters.
Separate Bearer secrets from raw TCP refusal
Declaring secret means REST clients owe Authorization: Bearer <token> headers. Omitting tokens produces HTTP 401 payloads after TCP establishes—emotionally resembling failure yet mechanically distinct from instant SYN refusal when nothing listens.
Laboratory debugging sometimes blanks secrets temporarily—rotate random strings before LAN binds widen. Clipboard smart quotes, mobile keyboards swapping apostrophes, or GUI truncation routinely desynchronize humans from machines.
curl http://127.0.0.1:9090/version \
-H "Authorization: Bearer YOUR_TOKEN"
Desktop GUIs regenerate YAML faster than notebooks
Verge-class shells stitch remote bundles atop local scaffolding; clicking merge may revert external-controller tighter than your manual widenings. Symptoms flip within seconds, encouraging false regression reports upstream.
Diff runtime exports versus editor buffers immediately after nightly upgrades—and cross-read our Verge onboarding article when GUI labels reshuffle quarterly.
Launching twins of the core—one headless systemd service plus one GUI-managed child—means only one occupies port 9090; the loser silently collapses despite splash screens implying success.
Feed panels accurate base URLs and trust stores
Panels require deliberate schemes (http, https, reverse-proxy suffixes). Corporate SSL inspection rewires browsers separately from curl trust stores; identical configs behave differently depending on tooling.
Metacube iterations cache stale endpoints aggressively; wiping site storage matches classic CDN busting choreography before accusing cores.
Operating-system choke points masquerading as regressions
Windows Defender, macOS prompts, iptables fronts, nftables wrappers, enforcing SELinux, or corporate MDM payloads may approve SOCKS binds while silently denying REST arrivals—produce identical refusal logs until you correlate allowlists.
Bridged VMs or NIC passthrough rigs sometimes address the wrong subnet when operators bookmark literal 127.0.0.1 from laptops physically distinct from daemon hosts—even though psyche insists they already opened Yacd yesterday.
HTTPS controllers, reverse proxies, and SPA shells
Some distributions optionally wrap REST API sockets behind TLS overlays or offload them onto Unix domain sockets surfaced through nginx snippets. Symptoms shift from brute refusal toward certificate warnings, websocket upgrade rejects, mismatched hosts inside SNI, or HTTP 502 bodies returned by impatient reverse proxies spinning faster than backends restart. Inspect whether your tutorial references plaintext http://127.0.0.1:9090 while the actual stack migrated to HTTPS on :9443 guarded by rotating ACME certs—panels keep retrying plaintext until boredom wins.
Single-page dashboards may embed API calls against absolute URLs burnt into build artifacts—after you widen binds or terminate TLS atop new hostnames those fetch paths still stab dead endpoints until you regenerate environment variables bundled with nightly builds.
Correlating supervisors, overlays, nightly imports
systemd watchers often prepend Environment= overrides loading profiles from guarded directories; meanwhile GUI exporters copy merged YAML beneath user-writable prefixes. Divergence means ss output disagrees between sudo versus regular sessions—you chase ghosts until you unify which binary truly launched.
Automation pulling remote subscriptions hourly may resurrect conservative external-controller excerpts unless you hoist personal overrides ahead of merges or pin dedicated override files untouched by CDN bundles.
Journaling not only syslog lines but annotated timestamps inside password vaults documenting each bind experiment rescues future selves when onboarding relatives across holidays.
When bridging containers or Compose stacks, hostname resolution favors service aliases—calling clash-core:9090 from adjacent containers differs materially from curling laptop loopback bookmarks.
Symptoms versus first corrective moves
| Observation | Likely layer | First corrective move |
|---|---|---|
| curl from localhost succeeds; LAN resets instantly | Loopback bind versus VLAN segregation | Widen binds carefully or tunnel via SSH; verify targeted IP |
| HTTP 401 with body | Bearer mismatch | Copy secret verbatim or regenerate pairings |
| No LISTEN socket | Dead service | Inspect journals, rerun ss loops, reopen GUI exports |
| Port hops nightly | Subscription merge overwriting toggles | Snapshoot runtime YAML timestamps |
| TLS alert unknown ca | Trust store | Separate from bind refusal after LISTEN confirms |
Where to read next on this site
Once REST listeners behave, widen context deliberately: skim the sprawling catalog inside our general Clash troubleshooting guide for mixed-port dramas unrelated to dashboards, revisit transparent TUN capture when split-tunnel quirks masquerade as API failures, and align LAN ergonomics beside SOCKS listeners using Windows or macOS LAN proxy ergonomics so widening external-controller parallels mixed-port tightening you already sanctioned.
If headless installs power your homelab, keep the systemd cadence beside native Mihomo + systemd packaging so journaling habits stay transferable between GUI rigs and unattended servers babysitting nightly subscription pulls.
Field narrative: diagnosing a LAN roommate rig without drama
Imagine a Mihomo-compatible Clash workstation sharing Ethernet with consoles and smart TVs—the operator tightens SOCKS on 192.168.8.102 while external-controller persists on loopback-only 127. Roommate laptops naturally aim browsers at typed host strings matching the SOCKS IP, misunderstandably expecting Yacd overlays to answer because marketing screenshots implied everything lived on mythical :9090. Screenshots seldom caption bind semantics, so empathize before lecturing DNS again.
Your troubleshooting sequence becomes sociological choreographed tech support: corroborate ss, demonstrate curl success from the operator seat, widen binds under explicit roommate consent documents, annotate firewall scopes in shared plaintext notes pinned beside router credentials, regenerate router DHCP reservations when laptops shuffle VLANs unknowingly thanks to docking stations toggling subnets, correlate MDM payloads intercepting corp laptops visiting home networks, babysit systemd reload storms triggered by unattended subscription pulls at 03:00 because cron forgot timezone offsets, babysit rogue docker-compose stacks spawning duplicate cores resurrected by startup hooks you swore retired last quarter.
Document every bind widening with dated rationale—even future you forgets motivations during bleary-eyed incident response. Screenshots anchored to journalctl excerpts reduce Slack ping-pong. When widening fails morally because threat models forbid LAN APIs, SSH port forwards become friendlier compromises than punching holes through ISP NAT without VPN backhaul.
Teaching roommates to run curl once feels nerdier than sharing TikTok hacks, yet it pays rent in reclaimed weekends—especially when helper scripts emit colored PASS/FAIL lines for JSON /version payloads while sanitizing Bearer tokens instead of stuffing them verbatim into ~/.bash_history you forgot to cloak with HISTCONTROL=ignoreboth.
Recap
Connection chasing Yacd or Metacube at 127.0.0.1:9090 reduces to aligning external-controller hosts, Bearer secrets regenerated by merges, OS firewall scopes, widening binds thoughtfully, journaling races, corroborating curl /version payloads, spotting TLS inspection interference, and reserving LAN exposure for trusted segments only.
Compared with brittle appliances hiding listeners, Mihomo-compatible Clash empowers operators who relish observability; polish that habit by leaning on curated installers from our download funnel instead of mirror roulette.