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 只读核查时,当前运行容器的 8080、9090 和 7890 仍绑定在 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
这里有三个重要设计:
8080/9090绑定宿主机127.0.0.1,无法从公网直接访问。/opt/metacubexd/data直接绑定到/data,便于备份和检查。- 健康检查请求
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:8080 或 0.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 KiB 或 651 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 的
/rulesAPI 返回{"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_TOKEN 和 CLASH_SECRET |
| 面板能打开、后端无法连接 | config.js、浏览器实际访问地址 |
127.0.0.1 指向浏览器电脑,或 9090 不可达 |
| Docker 显示 healthy,但无代理 | 日志与 9090/version |
默认健康检查只覆盖 8080 |
[kernel] errored |
GEO 文件大小、日志、监听端口 | geoip.metadb 损坏或下载中断 |
| YAML 有规则,页面规则为空 | 文件修改时间与 /rules |
内核没有重新加载最新配置 |
| Compose 已改但端口仍公网 | docker ps 和 docker 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.yaml和active.yaml.bak;profiles/;geoip.metadb;- 当前
docker-compose.yml和.env的安全备份。
回滚时应优先恢复 Compose、数据目录和原镜像版本,然后重新创建容器并执行完整验证清单。不要在未确认备份路径和内容的情况下覆盖生产 /opt/metacubexd/data。
十四、后续改进¶
- 将
8080/9090从0.0.0.0收敛到127.0.0.1。 - 立即轮换示例弱口令,真实凭据只放
.env或 Secret 管理系统。 - 云安全组只允许可信来源访问
7890,并配置代理认证。 - 固定经过验证的镜像版本或 digest,不直接依赖
latest。 - 定期备份
/opt/metacubexd/data,并验证可恢复性。 - 监控
9090/version,不要只监控8080页面。 - 对 GEO 数据库更新采用“下载到临时文件、校验、原子替换”的流程。