先選對映像檔:本文部署 all-in-one,不是單獨面板

這篇教學只處理一個目標:在已經安裝 Docker Engine 與 Docker Compose 外掛的 Linux 主機、NAS 或伺服器上,透過 MetaCubeXD 官方 all-in-one 容器執行 Mihomo。官方 MetaCubeXD README 將它定義為面板 UI、控制 agent 與依架構打包的 Mihomo 核心組合;同一個容器顯示介面、管理設定並監督核心,同時提供代理連接埠。

兩個名稱相近的映像檔不能混用。本文使用 ghcr.io/metacubex/metacubexd-server:latest,內含 UI、agent 與 Mihomo;ghcr.io/metacubex/metacubexd:latest 只提供靜態面板,必須連接到另外執行的 Mihomo,不能照本文期待它啟動核心。桌面安裝包又是另一種分發形式,也不在本文範圍。

映像檔包含內容適用情況
metacubexd-server面板、控制 agent、Mihomo 核心希望用一個 Compose 服務管理執行環境
metacubexd只有靜態面板,容器內 HTTP 連接埠為 80已有獨立 Mihomo,只需要 Web 介面

latest 是浮動標籤:它會隨專案發布指向新的映像內容,不是可重現的固定版本。首次部署可依官方範例使用,但更新前應記錄目前映像摘要,並替 /data 建立可還原備份。

準備目錄與兩個獨立密鑰,不把權杖寫進公開檔案

在主機新增只供這次部署使用的目錄,確認 docker version 與 docker compose version 都能正常回傳。本文不假設特定 Linux 發行版,也不把 Docker 安裝混進同一主題;主機尚未安裝 Docker 時,先依 Docker 對應發行版的官方文件完成安裝。

在部署目錄建立 .env,分別填入兩個不同的高強度隨機值。可以執行兩次 openssl rand -hex 32 產生候選值,再手動放進檔案;不要把輸出貼到工單、截圖或聊天。

CONTROL_TOKEN=replace-with-first-random-value
CLASH_SECRET=replace-with-second-random-value
TZ=Asia/Shanghai

CONTROL_TOKEN 用於保護 agent 的 /api/control/** 路由;CLASH_SECRET 是 Mihomo external-controller API 的密鑰,也是面板稍後連到 9090 時要填入的 Secret。兩者服務不同介面,不能為了方便設成相同值。官方 Compose 用 :? 檢查必填變數,任一項缺少時應讓部署停止,而不是無密鑰啟動。

CONTROL_TOKEN 不是面板登入密碼:all-in-one 頁面會將它注入同源瀏覽器請求,讓介面呼叫控制 agent。即使已設定此值,仍要用主機防火牆、可信區網 ACL 或帶驗證的反向代理限制面板存取。

把 .env 權限限制為管理帳號可讀,並排除於公開儲存庫和分享備份之外。若懷疑密鑰外洩,應輪替對應值、重建容器,並更新面板儲存的 Mihomo Secret。

依官方連接埠與可寫資料卷建立 compose.yaml

下面保留官方 docs/docker-compose.yml 的核心結構。命名資料卷掛載到 /data,保存 profiles、目前啟用的設定、Geo 資料和 fake-ip 等執行快取;該目錄必須可寫,唯讀掛載會讓核心以非零狀態結束。

services:
  metacubexd:
    image: ghcr.io/metacubex/metacubexd-server:latest
    container_name: metacubexd
    restart: unless-stopped
    environment:
      CONTROL_TOKEN: '${CONTROL_TOKEN:?Set CONTROL_TOKEN in .env}'
      CLASH_SECRET: '${CLASH_SECRET:?Set CLASH_SECRET in .env}'
      CONTROL_PORT: '8080'
      CLASH_API_PORT: '9090'
      MIXED_PORT: '7890'
      TZ: '${TZ:-UTC}'
    ports:
      - '8080:8080'
      - '9090:9090'
      - '7890:7890'
    volumes:
      - 'metacubexd-data:/data'
    healthcheck:
      test: ['CMD', 'wget', '-qO-', 'http://127.0.0.1:8080/api/control/health']
      interval: 30s
      timeout: 5s
      start_period: 10s
      retries: 3

volumes:
  metacubexd-data: {}
主機連接埠容器中的用途不可混淆之處
8080MetaCubeXD 面板與 /api/control agent API不是 Mihomo 的 Clash API
9090Mihomo external-controller API 與 WebSocket面板連接後端時指向此處
7890HTTP 與 SOCKS 共用的 mixed 代理連接埠應用程式必須明確使用,不會自動接管系統流量

Docker 發布連接埠不會覆蓋 Mihomo 的監聽策略。Mihomo 的 allow-lan 預設為 false;在 bridge 網路中,mixed 連接埠此時只監聽容器 loopback,僅寫 '7890:7890' 不會自動讓主機或區網用戶端連入代理。需要使用映射的代理連接埠時,必須在目前啟用的 profile 明確設定 allow-lan: true,再依實際網段限制 bind-address 與 lan-allowed-ips,並結合 authentication、Docker 綁定位址與主機防火牆。切換 profile 後要重新檢查這些值。

主機連接埠若已有程式占用,可只修改映射左側,例如 '18080:8080';此時容器仍監聽 8080,瀏覽器與主機 health 請求則要改用 18080。同理,只改 9090 或 7890 的左側時,面板 endpoint 或用戶端必須使用新的主機連接埠。若要修改容器內的 CONTROL_PORT,必須同步修改映射右側與 healthcheck URL;其他容器監聽值也要與映射右側成對。先用 docker compose config 查看展開結果;此命令會顯示展開後的真實密鑰,不要錄影、分享或存入公開日誌。確認無誤後再執行 docker compose up -d。

啟動後分三層檢查:容器、agent 與 Mihomo 核心

執行 docker compose up -d 後,先用 docker compose ps 確認容器沒有反覆重啟,再用 docker compose logs --tail=100 metacubexd 查看啟動階段。連接埠衝突、/data 不可寫、設定解析失敗與核心退出屬於不同問題,不能只因面板打得開就判斷全部正常。

  1. 確認容器正在執行,三個預期連接埠沒有綁定錯誤。
  2. 在主機執行 curl -fsS http://127.0.0.1:8080/api/control/health,檢查面板與 control agent;若映射左側已改成 18080,這裡也要改用 18080。
  3. 進入面板的核心區域,確認 Mihomo 狀態為 running,並查看獨立的 kernel 日誌。
  4. 啟用一份已驗證的 profile,確認代理群組、規則與日誌可正常讀取。
  5. 先確認目前 profile 已允許從容器外連入,再讓測試應用程式明確使用 http://127.0.0.1:7890 或相應 SOCKS 位址;若更改了映射左側,使用新的主機連接埠。

健康檢查有明確界線:/api/control/health 回傳 HTTP 200,只能證明面板與控制 agent 已啟動,不代表 Mihomo 核心正在執行,更不能證明訂閱、節點或代理出口可用。核心狀態、核心日誌與一筆實際代理請求都要分開確認。

容器反覆退出時,先保持 Compose、profile 與網路條件不變,只依日誌處理一個原因。/data 顯示唯讀就修正資料卷寫入條件;連接埠占用就停止衝突服務或成對修改映射;設定測試失敗則回到上一份可用 profile,不要同時開啟 TUN 或替換映像檔。

開啟 MetaCubeXD,將面板連接到 9090 後端

在可信網路開啟 http://<host>:8080。all-in-one 的控制 agent 會讓面板出現核心與 profile 管理能力;接著把面板的 Mihomo endpoint 設為 http://<host>:9090,Secret 填入 .env 中的 CLASH_SECRET。不要把 CONTROL_TOKEN 填到此欄,也不要把面板連接埠 8080 當成 Clash API 位址。

瀏覽器會把 8080 面板與 9090 API 視為不同來源。若面板載入正常卻顯示無法連接後端,先看瀏覽器主控台是否出現 CORS 或 Private Network Access(PNA)錯誤,再依 Mihomo 全域設定文件 與 MetaCubeXD README,在目前 profile 的 external-controller-cors 允許準確的面板來源,並只在需要存取私有網路時評估 allow-private-network: true。不要為了快速消除錯誤,就在公網環境長期保留萬用來源。

HTTPS 面板還有 Mixed Content 邊界。使用 HTTPS 載入 UI 時,瀏覽器通常會在 CORS 之前就阻擋對 http://<host>:9090 的不安全 HTTP/WebSocket 請求。此時應將 9090 置於支援 HTTPS/WSS 的反向代理之後,或按 Mihomo 文件設定 external-controller-tls 與憑證,面板 endpoint 使用可驗證的 https:// 位址;先解決 TLS/Mixed Content,再處理 CORS 與 PNA。

連接成功後,依序核對 Overview、Proxies、Connections、Rules、Logs 與 Config 頁面。下圖來自 MetaCubeXD 官方儲存庫的桌面 Overview 範例,只用於辨識面板配置;其中示範資料和後端位址不是你的實際節點,也不能取代本機狀態檢查。

最小驗收順序:先看 kernel 為 running,再載入 profile,接著選擇固定代理群組,最後從明確設定 7890 mixed 連接埠的應用程式發出請求。每一步只改一個條件,日誌才有診斷價值。

MetaCubeXD 官方專案中的桌面版總覽面板
MetaCubeXD 官方儲存庫的總覽介面範例。圖中的示範位址與流量資料不代表你的部署結果。

預設 Compose 只提供代理連接埠,TUN 是 Linux 進階覆寫

官方 Compose 明確標為 proxy-only。映射 7890:7890 只是在主機提供 mixed HTTP/SOCKS 代理連接埠;瀏覽器、命令列或其他裝置仍需把代理位址明確設為該主機與連接埠。啟動容器不會自動修改宿主機系統代理,也不會自動把整台機器、Docker 網路或區網裝置的流量透明送進 Mihomo。

確實需要 Linux 主機級 TUN 時,官方進階覆寫同時要求 network_mode: host、NET_ADMIN capability 與映射 /dev/net/tun,目前 Mihomo profile 還必須具有有效的 tun: 設定。三項容器權限與 profile 設定缺一不可。

services:
  metacubexd:
    network_mode: host
    cap_add:
      - NET_ADMIN
    devices:
      - '/dev/net/tun:/dev/net/tun'

host network 下 ports: 會被忽略:容器會直接在宿主機綁定 8080、9090 與 7890。啟用前先檢查防火牆、遠端管理通道與連接埠衝突,並準備主機主控台或其他還原入口;不要在只有一條遠端連線的正式主機上把 TUN 當作第一次測試。

發生全網中斷時,先停止 TUN 覆寫,恢復上一份 proxy-only Compose 與原 profile,再確認主機路由和 DNS 已還原。不要同時重裝 Docker、修改防火牆和更換設定;先回到能透過 7890 明確代理的基準,才可判斷問題是否出在 TUN 權限或路由。

限制 8080、9090 與 7890 的暴露範圍

Compose 中的 '8080:8080'、'9090:9090' 與 '7890:7890' 預設可能綁定主機所有介面。這適合先在受控環境驗證,但不代表可以直接公開到網際網路。8080 提供管理介面與 agent,9090 能控制 Mihomo,7890 是可轉送流量的代理入口,三者都需要保護。

  1. 僅本機使用時,將映射左側改為 127.0.0.1:8080:8080、127.0.0.1:9090:9090 與 127.0.0.1:7890:7890。
  2. 可信區網使用時,用主機防火牆或網路 ACL 只允許明確的管理與用戶端網段。
  3. 跨網路管理時,替控制面設計帶驗證與 TLS 的反向代理,並繼續限制 9090 和 7890,不要只保護網頁。
  4. 替 Mihomo API 保留獨立 CLASH_SECRET,定期檢查曾保存 endpoint 的瀏覽器與裝置。
  5. 修改後從允許與不允許的來源各測試一次,確認規則確實生效。

面板與 API 經不同網域或連接埠存取時,CORS 只決定瀏覽器是否允許請求,不等於身分驗證或網路隔離。允許來源、Secret、防火牆和反向代理各自處理不同層面,不能互相取代。

更新 latest 前記錄映像摘要,保留不刪除資料卷的還原路徑

官方更新命令為 docker compose pull && docker compose up -d,但 latest 會拉取當時的新內容。更新前先保存目前 Compose、脫敏後的設定基準和 /data 資料卷快照,再記錄執行中容器對應的儲存庫摘要:

docker image inspect "$(docker inspect metacubexd --format '{{.Image}}')" --format '{{index .RepoDigests 0}}'

把輸出的 ghcr.io/metacubex/metacubexd-server@sha256:... 保存到變更記錄。更新完成後,重新檢查容器狀態、agent health、kernel running、profile 載入和一筆真實代理請求。只看到新映像已下載,不代表移轉成功。

更新失敗時,停止新容器,把 Compose 的 image: 暫時改成剛才記錄的完整 digest,重新啟動並核對舊資料。若新版已經改動持久化內容,還要恢復更新前的資料卷快照。一般 docker compose down 會保留命名資料卷;還原時不要附加 -v,否則會刪除提供 /data 的資料卷。

完成標準:版本與映像來源可追溯、8080/9090/7890 只對預期範圍開放、/data 可寫且已備份、兩個 Secret 不混用、健康檢查與核心檢查都通過,並記錄 proxy-only 與 TUN 的界線。達成後才清理舊映像;還原觀察期結束前不要刪除資料卷快照。

官方來源與版本邊界

版本號、檔案名稱與介面文字可能隨專案更新而變動,操作前請以連結中的專案文件為準。