先选对镜像:本文部署的是 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 都能正常返回。本文不假定特定发行版,也不把 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 端口只监听容器回环地址,即使 Compose 已写 7890:7890,宿主机和局域网客户端也不一定能连入。需要通过映射端口使用代理时,应在当前激活的 profile 中明确设置 allow-lan: true,再按真实网段配置 bind-address 与 lan-allowed-ips,并结合 authentication、Docker 绑定地址和主机防火墙限制范围。切换 profile 后要重新确认这些字段。

如果主机端口已有程序占用,最简单的做法是只修改映射左侧,例如用 '18080:8080' 保持容器仍监听 8080;修改后,浏览器和宿主机健康检查都应访问左侧的 18080,同理,改过的 9090 或 7890 主机端口也是面板 endpoint 或客户端实际使用的端口。若连 CONTROL_PORT 等容器监听值也要修改,映射右侧必须改成相同端口;修改 CONTROL_PORT 时还必须同步修改 healthcheck URL 中的 8080。先用 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;若映射左侧已改,这里使用新的主机端口。
  3. 进入面板的内核区域,确认 Mihomo 状态为 running,并查看独立的 kernel 日志。
  4. 激活一份经过验证的 profile,确认代理组、规则和日志能正常读取。
  5. 确认活动 profile 已允许容器外部访问后,让测试应用明确使用 http://127.0.0.1:7890 或对应 SOCKS 地址;若 7890 的主机映射已改,使用新的左侧端口。

健康检查有明确边界:/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 错误,再按 Mihomo 全局配置文档 和 MetaCubeXD README,在当前 profile 的 external-controller-cors 中允许准确的面板来源。从更公开的地址空间访问私网 controller 时,还需按浏览器的 Private Network Access 要求评估 allow-private-network: true。不要为了快速消除报错就在公网环境长期保留通配来源。

HTTPS 面板不能继续直连明文 http://<host>:9090。浏览器会在 CORS 之前按 Mixed Content 规则拦截 HTTPS 页面对 HTTP/WS 后端的请求。将面板放到 TLS 反向代理后时,9090 API 也必须经过支持 HTTPS/WSS 的反向代理,或按 Mihomo 文档配置 external-controller-tls 与证书;面板 endpoint 应改填 https:// 地址。先打通 TLS 通道,再处理精确 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 的边界已经记录。达到这些条件后再清理旧镜像;回退观察期结束前不要删除卷快照。

官方来源与版本边界

版本号、文件名和界面文字可能随项目更新而变化,操作前请以链接中的项目文档为准。