Mihomo Dashboard, The Official One, XD
Desktop Screenshots
| Overview | Proxies |
|---|---|
![]() |
![]() |
| Connections | Rules |
|---|---|
![]() |
![]() |
| Logs | Config |
|---|---|
![]() |
![]() |
Mobile Screenshots
| Overview | Proxies | Connections |
|---|---|---|
![]() |
![]() |
![]() |
| Rules | Logs | Config |
|---|---|---|
![]() |
![]() |
![]() |
| Platform | URL |
|---|---|
| GitHub Pages | https://metacubex.github.io/metacubexd |
| Cloudflare Pages | https://metacubexd.pages.dev |
metacubexd ships in three forms from one codebase:
| Form | Who hosts the UI | Who runs the kernel | Best for |
|---|---|---|---|
| Hosted panel (classic) | gh-pages / your static host | your own remote mihomo | pointing a browser at an existing mihomo |
| Desktop app | the app (bundled) | the app supervises a bundled mihomo | a single machine, zero setup |
| All-in-one server | the Docker image | the container's bundled mihomo | a router / NAS / VPS |
The classic pure-panel mode still works against any remote mihomo — the kernel-control and profile features simply stay hidden when the dashboard isn't talking to a bundled agent.
Enable the external-controller in your mihomo config.yaml:
external-controller: 0.0.0.0:9090
Then either open the hosted dashboard and enter your mihomo {url, secret}:
| Platform | URL |
|---|---|
| GitHub Pages | https://metacubex.github.io/metacubexd |
| Custom domain | https://d.metacubex.one |
…or self-host the static assets via external-ui:
# Clone the prebuilt gh-pages branch
git clone https://github.com/metacubex/metacubexd.git -b gh-pages /etc/mihomo/ui
# Set external-ui in your config:
# external-ui: /etc/mihomo/ui
# Update to the latest build later:
git -C /etc/mihomo/ui pull -r
Download the installer for your platform from the latest release:
| OS | Arch | File |
|---|---|---|
| macOS (Apple Silicon) | arm64 | MetaCubeXD-<version>-mac-arm64.dmg |
| macOS (Intel) | x64 | MetaCubeXD-<version>-mac-x64.dmg |
| Windows | x64 / arm64 | MetaCubeXD-<version>-win-<arch>.exe |
| Linux | x64 / arm64 | MetaCubeXD-<version>-linux-<arch>.AppImage or .deb |
The desktop app bundles its own mihomo kernel and auto-configures the local endpoint — you don't enter any address. Manage profiles, edit configs, and start/stop the kernel directly inside the app.
These builds are unsigned. Each OS warns about unidentified developers; the steps below are expected and safe.
macOS — Gatekeeper blocks unsigned apps ("MetaCubeXD is damaged and can't
be opened"). After dragging the app to /Applications, strip the quarantine
attribute:
xattr -dr com.apple.quarantine /Applications/MetaCubeXD.app
…or right-click the app → Open → Open in the confirmation dialog.
Windows — SmartScreen shows "Windows protected your PC". Click More info → Run anyway.
Linux — make the AppImage executable, then run it:
chmod +x MetaCubeXD-*-linux-*.AppImage
./MetaCubeXD-*-linux-*.AppImage
# or install the .deb:
sudo dpkg -i MetaCubeXD-*-linux-*.deb
System proxy / TUN are advanced features. The app defaults to a mixed proxy port (no elevation). System proxy and TUN both require privileges; see TUN mode (desktop) for the one-time privileged-helper install and what the unsigned build means for it.
TUN takes over all machine traffic at the network layer, so every app is routed through mihomo without per-app proxy settings (unlike the mixed proxy port, which only catches apps you point at it). Because routing the whole system needs root/admin, the desktop app installs a small privileged helper the first time you enable TUN — a background service that performs the privileged network setup on the app's behalf. Enabling TUN therefore prompts for an administrator authorization the first time (the OS elevation dialog). The helper persists across sessions, so later enables don't re-prompt for install.
These builds are unsigned, so you hit the unidentified-developer warnings twice: once launching the app (see above — macOS Gatekeeper / Windows SmartScreen / Linux "unknown publisher"), and again when the helper install asks for administrator authorization. Both are expected. Allow them the same way you allowed the app itself.
Per-OS notes
osascript "wants to make changes" administrator
prompt. The kernel binds the system's utun interface.wintun.dll alongside the kernel, which
mihomo's wintun backend needs — no separate download.pkexec (the GNOME/PolicyKit authorization dialog). On a headless
box without a Polkit agent you may need to install/enable one.If TUN takes down your network
TUN reroutes default traffic, so a bad config or a stuck tunnel can drop connectivity. Two recoveries:
Status: TUN is new. It's verified at the unit-test / command-generation / packaging level, but the real install, elevation, and per-OS tunnel behavior still need smoke testing on actual macOS / Windows / Linux machines. If you hit a problem, please open an issue with your OS, the exact prompt you saw, and whether "Recover network" restored connectivity.
The metacubexd-server image bundles the dashboard UI, the control agent, and
a per-arch mihomo kernel. One container serves the panel, supervises the
kernel, and exposes the proxy.
# compose.yaml — proxy-only by default; TUN is an advanced override
services:
metacubexd:
image: ghcr.io/metacubex/metacubexd-server:latest
restart: unless-stopped
environment:
CONTROL_TOKEN: 'change-me-control'
CLASH_SECRET: 'change-me-clash'
CONTROL_PORT: '8080'
CLASH_API_PORT: '9090'
MIXED_PORT: '7890'
TZ: 'Asia/Shanghai'
ports:
- '8080:8080' # dashboard UI + /api/control agent API
- '9090:9090' # mihomo Clash API + WebSocket (UI endpoint target)
- '7890:7890' # mixed proxy port
volumes:
- 'metacubexd-data:/data'
volumes:
metacubexd-data: {}
docker compose up -d
# Update
docker compose pull && docker compose up -d
Open http://<host>:8080 for the dashboard. The control agent unlocks the
kernel/profile UI automatically: the server injects CONTROL_TOKEN into the
same-origin page, so the dashboard authenticates its /api/control probe
without you entering the token anywhere.
Point the UI endpoint at the kernel. The dashboard talks to mihomo's Clash API directly (never proxied), so set the endpoint to:
| Field | Value |
|---|---|
| URL | http://<host>:9090 |
| Secret | the CLASH_SECRET you set above |
| Variable | Default | Purpose |
|---|---|---|
CONTROL_TOKEN |
(none) | Bearer token guarding the control agent (/api/control/**); also accepted as ?token= for the SSE log stream. Set a strong value. |
CLASH_SECRET |
(none) | Secret for mihomo's Clash API (external-controller). Use this as the UI endpoint's Secret. |
CONTROL_PORT |
8080 |
Port serving the dashboard UI + control agent API. |
CLASH_API_PORT |
9090 |
Port for mihomo's Clash API + WebSocket. The UI endpoint targets this port. |
MIXED_PORT |
7890 |
mihomo mixed (HTTP + SOCKS) proxy port. |
TZ |
(container default) | Timezone for logs/scheduling, e.g. Asia/Shanghai. |
The named volume mounts /data, which holds your profiles, the active config,
and the kernel's geo / fake-ip caches. It must be writable — a read-only
data dir makes the kernel exit non-zero.
TUN needs NET_ADMIN, the /dev/net/tun device, and host networking. Override
the service:
services:
metacubexd:
image: ghcr.io/metacubex/metacubexd-server:latest
restart: unless-stopped
network_mode: host
cap_add:
- NET_ADMIN
devices:
- '/dev/net/tun:/dev/net/tun'
environment:
CONTROL_TOKEN: 'change-me-control'
CLASH_SECRET: 'change-me-clash'
volumes:
- 'metacubexd-data:/data'
volumes:
metacubexd-data: {}
With network_mode: host the ports: mapping is ignored — the container
binds 8080/9090/7890 directly on the host. Enable a tun: block in your
profile's mihomo config for the tunnel to come up.
$ claude mcp add metacubexd \
-- python -m otcore.mcp_server <graph>