跳转至

MetaCubeXD 与 Mihomo Docker 部署及排障全记录

本文汇总 infra.wlcb 上 MetaCubeXD All-in-One 容器的部署流程、访问方式和实际排障经验。目标形态如下:

  • MetaCubeXD 控制面板 8080 仅监听服务器本机;
  • Mihomo API 9090 仅监听服务器本机;
  • Mixed Proxy 7890 按需对外开放,但必须限制来源 IP;
  • 数据持久化到宿主机 /opt/metacubexd/data
  • 通过 SSH 隧道访问面板和 Mihomo API;
  • 健康检查覆盖真正的 Mihomo API,而不只是网页服务。

生产现状与目标配置不同

2026-09-01 只读核查时,当前运行容器的 808090907890 仍绑定在 0.0.0.0,并使用示例弱口令。本文给出的是推荐目标配置。变更生产端口、凭据或挂载前,应按变更流程确认访问方式和回滚方案。

一、组件和端口关系

端口 组件 用途 推荐暴露范围
8080 MetaCubeXD Node 服务 Web 面板和控制接口 127.0.0.1
9090 Mihomo Clash/Mihomo REST API 127.0.0.1
7890 Mihomo HTTP/SOCKS Mixed Proxy 按需开放并限制来源 IP

容器里还有两个容易混淆的凭据:

环境变量 使用方 典型用途
CONTROL_TOKEN MetaCubeXD 控制服务 管理内置内核和配置
CLASH_SECRET Mihomo API 面板连接 9090 时填写的 Secret

CONTROL_TOKEN 填进 Mihomo Secret 输入框,会得到“secret 被拒绝”。

二、推荐目录结构

服务器上的部署目录:

/home/lixie/clash-metacubexd/
├── docker-compose.yml
└── .env

宿主机持久化目录:

/opt/metacubexd/data/
├── active.yaml
├── active.yaml.bak
├── cache.db
├── geoip.metadb
└── profiles/

创建目录:

sudo install -d -m 0700 /opt/metacubexd/data
sudo install -d -m 0700 /home/lixie/clash-metacubexd

三、准备凭据

不要使用 change-me-* 示例值。可以在本地生成随机凭据:

openssl rand -hex 32
openssl rand -hex 32

将结果写入部署目录的 .env,并限制权限:

CONTROL_TOKEN=<随机 CONTROL_TOKEN>
CLASH_SECRET=<随机 CLASH_SECRET>
chmod 600 /home/lixie/clash-metacubexd/.env

Note

更换 CLASH_SECRET 后,MetaCubeXD 已保存的后端连接也要使用新 Secret。不要把真实凭据写入 Git 仓库、截图或运维文档。

四、推荐 Docker Compose

/home/lixie/clash-metacubexd/docker-compose.yml

services:
  metacubexd:
    image: ghcr.io/metacubex/metacubexd-server:latest
    container_name: metacubexd
    restart: unless-stopped

    environment:
      CONTROL_TOKEN: ${CONTROL_TOKEN}
      CLASH_SECRET: ${CLASH_SECRET}
      DEFAULT_BACKEND_URL: http://127.0.0.1:9090
      TZ: Asia/Shanghai

    ports:
      - "127.0.0.1:8080:8080"
      - "127.0.0.1:9090:9090"
      - "7890:7890"

    volumes:
      - /opt/metacubexd/data:/data

    healthcheck:
      test:
        - CMD-SHELL
        - 'wget -qO- --header="Authorization: Bearer $${CLASH_SECRET}" http://127.0.0.1:9090/version >/dev/null || exit 1'
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 30s

这里有三个重要设计:

  1. 8080/9090 绑定宿主机 127.0.0.1,无法从公网直接访问。
  2. /opt/metacubexd/data 直接绑定到 /data,便于备份和检查。
  3. 健康检查请求 9090/version,确保 Mihomo 本身可用。

生产环境最好把 latest 替换为经过验证的版本标签或镜像 digest,避免重新拉取镜像时引入未经验证的变更。

五、首次部署

进入部署目录并检查最终渲染配置:

cd /home/lixie/clash-metacubexd
sudo docker compose config

确认无误后启动:

sudo docker compose pull
sudo docker compose up -d

检查容器、挂载和端口:

sudo docker ps --filter name=metacubexd \
  --format '{{.Names}} | {{.Status}} | {{.Ports}}'

sudo docker inspect metacubexd --format '{{json .Mounts}}'

目标端口输出应包含:

127.0.0.1:8080->8080/tcp
127.0.0.1:9090->9090/tcp
0.0.0.0:7890->7890/tcp

如果 docker ps 仍显示 0.0.0.0:80800.0.0.0:9090,说明新端口配置没有作用到当前容器,需要重新创建:

sudo docker compose up -d --force-recreate

六、通过 SSH 隧道访问

在管理电脑执行:

ssh \
  -L 8080:127.0.0.1:8080 \
  -L 9090:127.0.0.1:9090 \
  infra.wlcb

然后访问:

http://127.0.0.1:8080

后端地址填写:

http://127.0.0.1:9090

Secret 填写 CLASH_SECRET,不是 CONTROL_TOKEN

为什么公网面板不能配 127.0.0.1:9090

DEFAULT_BACKEND_URL 会写入前端配置,由浏览器直接访问。如果浏览器打开:

http://<SERVER_PUBLIC_IP>:8080

那么前端里的 127.0.0.1:9090 指向的是浏览器所在电脑,不是服务器。结果就是面板能打开,但显示“后端无法连接”。

因此只能选择以下一种拓扑:

  • 公网面板:后端也必须有浏览器可达的地址,不推荐直接暴露明文 9090
  • 私有面板:通过 SSH/VPN 同时转发 8080/9090,后端使用 127.0.0.1:9090
  • HTTPS 反向代理:通过同域受保护路径代理 API,需要额外设计反向代理和认证。

七、从 Docker 命名卷迁移到宿主机目录

旧部署可能使用命名卷:

clash-metacubexd_metacubexd-data

切换为 /opt/metacubexd/data:/data 前必须迁移数据,否则新容器看到的是空目录。迁移步骤:

sudo install -d -m 0700 /opt/metacubexd/data

sudo docker run --rm \
  -v clash-metacubexd_metacubexd-data:/source:ro \
  -v /opt/metacubexd/data:/target \
  alpine sh -c 'cp -a /source/. /target/'

复制完成后,先核对关键文件:

sudo find /opt/metacubexd/data -maxdepth 2 -type f \
  -printf '%s %p\n' | sort -n

再更新 Compose 并重新创建容器。确认新绑定目录运行正常前,不要删除旧命名卷,以便回滚。

八、GEO 数据库损坏导致内核启动失败

现象

容器页面正常,但日志显示:

[kernel] starting bundled mihomo on boot…
Listening on http://[::]:8080
[kernel] errored (pid 20)

容器内只有 8080 在监听,7890/9090 都不存在。进一步检查时,Mihomo 报:

MMDB invalid, remove and download

根因

服务器从 GitHub 下载 geoip.metadb 时速度过慢或连接中断,只留下一个很小的截断文件。Mihomo 每次启动都会判断文件无效、删除并重新下载;下载再次中断后形成启动失败循环。

本次事故中观察到的损坏文件约为 240 KiB651 KiB。2026-08-31 当时的完整文件为 8,597,075 字节,但上游文件大小和哈希会变化,不能把这个数值当成永久标准。

安全修复方法

在网络状况较好的管理电脑下载:

curl -fL \
  --retry 5 \
  --retry-all-errors \
  --connect-timeout 10 \
  https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb \
  -o /tmp/geoip.metadb

stat /tmp/geoip.metadb
shasum -a 256 /tmp/geoip.metadb

上传服务器并再次核对:

scp /tmp/geoip.metadb infra.wlcb:/tmp/geoip.metadb

ssh infra.wlcb \
  'stat /tmp/geoip.metadb && sha256sum /tmp/geoip.metadb'

先写入临时文件,再在同一文件系统内原子替换:

sudo install -m 0644 \
  /tmp/geoip.metadb \
  /opt/metacubexd/data/geoip.metadb.new

sudo mv \
  /opt/metacubexd/data/geoip.metadb.new \
  /opt/metacubexd/data/geoip.metadb

sudo docker restart metacubexd

不要把 curl 输出直接写入正式的 geoip.metadb。下载中断时,这种做法会把原本可用的文件覆盖成残缺文件。

九、规则文件存在,但面板看不到规则

本次出现过以下状态:

  • /data/active.yaml 中存在大量 rules
  • Mihomo 的 /rules API 返回 {"rules":[]}
  • active.yaml 的修改时间晚于 Mihomo 启动时间。

原因是配置文件已经更新,但运行中的 Mihomo 没有重新加载。规则页面读取的是 Mihomo 运行时 API,不是直接读取 YAML 文件。

检查时间关系和运行时规则:

sudo docker inspect metacubexd --format '{{.State.StartedAt}}'
sudo stat /opt/metacubexd/data/active.yaml

curl -H 'Authorization: Bearer <CLASH_SECRET>' \
  http://127.0.0.1:9090/rules

如确认运行时仍是旧配置,可通过面板重载内核,或在变更窗口重启容器:

sudo docker restart metacubexd

本次重启后成功加载 285 条规则。

十、不要在生产 /data 上并行运行配置测试

下面的命令看似只是配置检查:

mihomo -d /data -t -f /data/active.yaml

但它并不完全只读。Mihomo 发现无效 MMDB 时可能删除文件并重新下载;第二个进程也可能和生产实例争用数据文件、端口或内存。

更稳妥的原则:

  • 优先读取现有进程日志、启动参数和 API;
  • 如需测试,把配置与依赖复制到独立临时目录;
  • 测试实例不要共享生产环境可写的 /data
  • 校验成功后,再通过原子替换更新生产文件。

十一、排障速查表

现象 优先检查 常见原因
secret 被拒绝 /version 带 Bearer Token 混用了 CONTROL_TOKENCLASH_SECRET
面板能打开、后端无法连接 config.js、浏览器实际访问地址 127.0.0.1 指向浏览器电脑,或 9090 不可达
Docker 显示 healthy,但无代理 日志与 9090/version 默认健康检查只覆盖 8080
[kernel] errored GEO 文件大小、日志、监听端口 geoip.metadb 损坏或下载中断
YAML 有规则,页面规则为空 文件修改时间与 /rules 内核没有重新加载最新配置
Compose 已改但端口仍公网 docker psdocker inspect 只改文件,没有重新创建容器
切换绑定目录后配置丢失 docker inspect .Mounts 命名卷数据没有迁移

十二、变更后验证清单

1. 容器和真实端口映射

sudo docker ps --filter name=metacubexd \
  --format '{{.Names}} | {{.Status}} | {{.Ports}}'

2. 实际挂载

sudo docker inspect metacubexd --format '{{json .Mounts}}'

3. 持久化文件

sudo find /opt/metacubexd/data -maxdepth 2 -type f \
  -printf '%s %p\n' | sort -n

4. Mihomo API

curl -H 'Authorization: Bearer <CLASH_SECRET>' \
  http://127.0.0.1:9090/version

5. 规则数量

curl -sS \
  -H 'Authorization: Bearer <CLASH_SECRET>' \
  http://127.0.0.1:9090/rules \
  | jq '.rules | length'

6. 监听端口和日志

sudo docker exec metacubexd \
  sh -lc 'ss -lntp 2>/dev/null || netstat -lntp'

sudo docker logs --timestamps --tail 100 metacubexd

成功状态应同时满足:

  • 日志最新一次启动出现 [kernel] running
  • 9090/version 返回 HTTP 200;
  • /rules 返回预期数量的规则;
  • 8080/9090 只绑定 127.0.0.1
  • Docker 实际挂载源为 /opt/metacubexd/data
  • 重启容器后上述检查仍然通过。

十三、备份与回滚

变更前备份绑定目录:

sudo tar -C /opt/metacubexd \
  -czf "/opt/metacubexd/data-backup-$(date +%Y%m%d-%H%M%S).tar.gz" \
  data

至少保留以下内容:

  • active.yamlactive.yaml.bak
  • profiles/
  • geoip.metadb
  • 当前 docker-compose.yml.env 的安全备份。

回滚时应优先恢复 Compose、数据目录和原镜像版本,然后重新创建容器并执行完整验证清单。不要在未确认备份路径和内容的情况下覆盖生产 /opt/metacubexd/data

十四、后续改进

  1. 8080/90900.0.0.0 收敛到 127.0.0.1
  2. 立即轮换示例弱口令,真实凭据只放 .env 或 Secret 管理系统。
  3. 云安全组只允许可信来源访问 7890,并配置代理认证。
  4. 固定经过验证的镜像版本或 digest,不直接依赖 latest
  5. 定期备份 /opt/metacubexd/data,并验证可恢复性。
  6. 监控 9090/version,不要只监控 8080 页面。
  7. 对 GEO 数据库更新采用“下载到临时文件、校验、原子替换”的流程。
回到页面顶部