Ubuntu Server 24.04 使用 Docker Compose 部署 Mihomo 完整指南
作者:Dafang
日期:2026-03-23
标签:Mihomo · Docker · Ubuntu · 代理 · 宝塔面板
前言
Mihomo(原 Clash.Meta)是目前功能最强大的代理内核之一,支持 TUN 透明代理、多种协议(VLESS/VMess/Trojan/SS)、规则分流等高级特性。本文记录了在 Ubuntu Server 24.04 上通过 Docker Compose 完整部署 Mihomo 的全过程,包括订阅转换、Web 控制面板、以及通过宝塔面板实现订阅自动更新。
环境说明
一、创建目录结构
mkdir -p /www/dk_project/dk_app/dk_mihomo/config
cd /www/dk_project/dk_app/dk_mihomo
目录规划:
/www/dk_project/dk_app/dk_mihomo/
├── docker-compose.yml # 服务编排文件
├── update_config.sh # 订阅更新脚本
└── config/
├── config.yaml # Mihomo 主配置(由订阅转换生成)
└── metacubexd/ # Web UI Caddy 配置(自动生成)
二、获取订阅配置
机场提供的订阅链接通常返回 Base64 编码的节点列表,Mihomo 无法直接使用,需要通过订阅转换器转为 Clash/Mihomo 格式的 YAML。
方法:使用公共订阅转换器
SUBSCRIBE_URL="你的机场订阅链接"
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$SUBSCRIBE_URL'))")
curl -L "https://api.v1.mk/sub?target=clash&url=${ENCODED}&insert=false" \
-o /www/dk_project/dk_app/dk_mihomo/config/config.yaml
验证转换结果:
head -5 /www/dk_project/dk_app/dk_mihomo/config/config.yaml
正确的配置文件开头应该是标准 YAML 格式:
port: 7890
mixed-port: 7890
allow-lan: true
mode: rule
...
常用公共转换器:
https://api.v1.mk/sub?target=clash&url=<订阅链接>
https://sub.xeton.dev/sub?target=clash&url=<订阅链接>如果某个转换器返回 520 等错误,换另一个即可。
三、编写 docker-compose.yml
# /www/dk_project/dk_app/dk_mihomo/docker-compose.yml
services:
mihomo:
container_name: mihomo
image: metacubex/mihomo:latest
restart: always
pid: host # 共享宿主机 PID,TUN 模式感知进程
ipc: host # 共享宿主机 IPC
network_mode: host # 使用宿主机网络栈,TUN 模式必须
cap_add:
- ALL # 赋予完整权限(含 NET_ADMIN、NET_RAW)
security_opt:
- apparmor=unconfined # 关闭 Ubuntu AppArmor 限制
volumes:
- ./config:/root/.config/mihomo
- /dev/net/tun:/dev/net/tun # 挂载 TUN 虚拟网卡设备
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
metacubexd:
container_name: metacubexd
image: ghcr.io/metacubex/metacubexd
restart: always
network_mode: bridge
ports:
- '9097:80' # 浏览器访问 http://服务器IP:9097
volumes:
- ./config/metacubexd:/config/caddy
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
关键配置项说明
四、启动服务
cd /www/dk_project/dk_app/dk_mihomo
# 拉取镜像并启动
docker compose up -d
# 查看启动日志
docker compose logs -f mihomo
查看服务状态:
docker compose ps
五、验证代理可用性
# 测试 HTTP 代理
curl -x http://127.0.0.1:7890 https://www.google.com -I
成功响应示例:
HTTP/1.1 200 Connection established
HTTP/2 200
...
server: gws
看到 Connection established 和 HTTP/2 200 即表示代理正常工作。
六、访问 Web 控制面板
浏览器打开:
http://服务器IP:9097
在面板中填写:
API 地址:
http://服务器IP:9090密码:
config.yaml中secret字段的值
面板功能包括:节点切换、延迟测试、实时流量、规则查看等。
七、宝塔面板计划任务配置
三个自动更新任务均采用直接在宝塔计划任务脚本框中填写脚本内容的方式,无需创建外部 sh 文件,管理更集中。所有脚本输出到 stdout,宝塔日志界面可直接查看。
注意:宝塔计划任务(cron)不会自动加载
/etc/profile.d/,所以脚本中凡是依赖特定命令路径的地方,均使用绝对路径,避免command not found。
进入 宝塔面板 → 计划任务 → 添加任务,依次添加以下三个任务。
7.1 任务一:Mihomo 订阅更新
脚本内容:
#!/bin/bash
export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
SUBSCRIBE_URL="你的机场订阅链接"
CONFIG_PATH="/www/dk_project/dk_app/dk_mihomo/config/config.yaml"
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$SUBSCRIBE_URL'))")
echo "========================================"
echo " Mihomo 订阅更新"
echo " $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始拉取订阅..."
curl -sL "https://api.v1.mk/sub?target=clash&url=${ENCODED}&insert=false" \
-o $CONFIG_PATH
if [ $? -eq 0 ] && [ -s $CONFIG_PATH ]; then
NODE_COUNT=$(grep -c "^ - name:" $CONFIG_PATH 2>/dev/null || echo "未知")
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 订阅拉取成功,节点数:${NODE_COUNT}"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 正在重启 mihomo..."
docker restart mihomo
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✓ 完成"
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✗ 订阅拉取失败,保留原配置"
exit 1
fi
日志输出示例:
========================================
Mihomo 订阅更新
2026-03-23 03:00:00
========================================
[2026-03-23 03:00:01] 开始拉取订阅...
[2026-03-23 03:00:03] 订阅拉取成功,节点数:32
[2026-03-23 03:00:03] 正在重启 mihomo...
[2026-03-23 03:00:05] ✓ 完成
7.2 任务二:Claude Code 更新
Claude Code 安装在 /root/.local/bin/claude(通过 npm 全局安装,路径不在宝塔 Node.js 目录下)。
脚本内容:
#!/bin/bash
export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
# Claude Code 实际路径(通过 which claude 确认)
CLAUDE_BIN=/root/.local/bin/claude
echo "========================================"
echo " Claude Code 更新"
echo " $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"
if [ ! -f "$CLAUDE_BIN" ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✗ 找不到 claude 命令:${CLAUDE_BIN}"
exit 1
fi
OLD_VER=$($CLAUDE_BIN --version 2>/dev/null || echo "未知")
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 当前版本:${OLD_VER}"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始更新..."
$CLAUDE_BIN update
EXIT_CODE=$?
NEW_VER=$($CLAUDE_BIN --version 2>/dev/null || echo "未知")
if [ $EXIT_CODE -eq 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✓ 更新完成,当前版本:${NEW_VER}"
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✗ 更新失败(exit code: ${EXIT_CODE}),当前版本:${OLD_VER}"
exit 1
fi
日志输出示例:
========================================
Claude Code 更新
2026-03-23 04:00:00
========================================
[2026-03-23 04:00:01] 当前版本:2.1.81 (Claude Code)
[2026-03-23 04:00:02] 开始更新...
Claude Code is up to date (2.1.81)
[2026-03-23 04:00:15] ✓ 更新完成,当前版本:2.1.81 (Claude Code)
7.3 任务三:OpenClaw 更新
OpenClaw 通过宝塔 Node.js 环境安装,路径为 /www/server/nodejs/v24.14.0/bin/openclaw。
脚本内容:
#!/bin/bash
export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
# 宝塔 Node.js 路径(openclaw 依赖 node 运行时)
export NODE_HOME=/www/server/nodejs/v24.14.0
export PATH=$NODE_HOME/bin:$PATH
OPENCLAW_BIN=/www/server/nodejs/v24.14.0/bin/openclaw
echo "========================================"
echo " OpenClaw 更新"
echo " $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"
if [ ! -f "$OPENCLAW_BIN" ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✗ 找不到 openclaw 命令:${OPENCLAW_BIN}"
exit 1
fi
OLD_VER=$($OPENCLAW_BIN --version 2>/dev/null || echo "未知")
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 当前版本:${OLD_VER}"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始更新..."
$OPENCLAW_BIN update
EXIT_CODE=$?
NEW_VER=$($OPENCLAW_BIN --version 2>/dev/null || echo "未知")
if [ $EXIT_CODE -eq 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✓ 更新完成,当前版本:${NEW_VER}"
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✗ 更新失败(exit code: ${EXIT_CODE}),当前版本:${OLD_VER}"
exit 1
fi
日志输出示例:
========================================
OpenClaw 更新
2026-03-23 04:10:00
========================================
[2026-03-23 04:10:01] 当前版本:0.8.1
[2026-03-23 04:10:02] 开始更新...
[2026-03-23 04:10:20] ✓ 更新完成,当前版本:0.8.2
三个任务执行时间错开 10 分钟,避免同时请求网络资源互相干扰,也方便在日志里区分各任务的输出。
八、宿主机代理环境全局配置
Mihomo 启动后,宿主机本身的各个工具需要分别配置才能走代理,原因是不同工具读取代理的方式不同,没有统一的全局入口。
8.1 Shell 环境变量(终端 curl / wget / git 等)
写入 /etc/profile.d/ 使所有用户登录终端自动生效:
cat > /etc/profile.d/proxy.sh << 'EOF'
export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
export HTTP_PROXY=http://127.0.0.1:7890
export HTTPS_PROXY=http://127.0.0.1:7890
export no_proxy=localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8
EOF
# 当前终端立即生效
source /etc/profile.d/proxy.sh
# 验证
curl https://www.google.com -I --max-time 5
注意:
/etc/profile.d/只对交互式登录 Shell 生效,cron 计划任务、systemd 服务均不会自动读取。
若宝塔计划任务脚本需要走代理,在脚本头部手动加上:
#!/bin/bash
export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
# ... 后续命令
8.2 Docker Daemon 代理(docker pull 拉取镜像)
Docker 守护进程有独立的代理配置,Shell 环境变量对其无效:
mkdir -p /etc/systemd/system/docker.service.d
cat > /etc/systemd/system/docker.service.d/proxy.conf << 'EOF'
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:7890"
Environment="HTTPS_PROXY=http://127.0.0.1:7890"
Environment="NO_PROXY=localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8"
EOF
# 重载配置并重启 Docker
systemctl daemon-reload
systemctl restart docker
# 验证
docker info | grep -i proxy
成功后 docker info 应显示:
HTTP Proxy: http://127.0.0.1:7890
HTTPS Proxy: http://127.0.0.1:7890
No Proxy: localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8
8.3 npm 代理
npm 有独立的代理配置,与系统环境变量无关:
# 设置代理
npm config set proxy http://127.0.0.1:7890
npm config set https-proxy http://127.0.0.1:7890
# 验证
npm config get proxy
npm config get https-proxy
# 测试(安装一个小包试试)
npm install -g npm@latest
如需取消 npm 代理(切换回直连或镜像源):
npm config delete proxy
npm config delete https-proxy
# 国内可改用淘宝镜像替代代理
npm config set registry https://registry.npmmirror.com
npm 代理与 registry 镜像源可以独立配置,互不影响。代理优先级高于 registry。
8.4 各工具代理生效范围汇总
8.5 局域网其他设备使用代理
Mihomo 配置中 allow-lan: true 开启后,局域网内其他设备可直接接入:
# 其他 Linux / macOS / WSL 机器
export http_proxy=http://服务器IP:7890
export https_proxy=http://服务器IP:7890
export no_proxy=localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8
Windows 在「设置 → 网络和 Internet → 代理」中填写服务器 IP 和端口 7890 即可。
九、常用管理命令
# 查看实时日志
docker compose logs -f mihomo
# 重启服务
docker compose restart mihomo
# 热重载配置(不重启容器,需要 config.yaml 中设置了 secret)
curl -X PUT http://127.0.0.1:9090/configs \
-H "Authorization: Bearer 你的secret" \
-H "Content-Type: application/json" \
-d '{"path": "/root/.config/mihomo/config.yaml"}'
# 更新镜像
docker compose pull && docker compose up -d
# 停止服务
docker compose down
总结
整个部署流程的关键点:
订阅转换:机场 Base64 订阅需要经过转换器转为 Clash YAML 格式
TUN 模式权限:
pid/ipc: host+cap_add: ALL+apparmor=unconfined三者缺一不可Web UI 分离部署:metacubexd 用 bridge 网络单独运行,通过 API 端口连接 Mihomo
自动更新:宝塔计划任务 + Shell 脚本,日志直接输出到 stdout 方便面板查看
代理配置各自独立:Shell 环境变量 / Docker Daemon / npm 需要分别配置,没有统一入口
整套方案在内网服务器上稳定运行,局域网内所有设备均可通过 7890 端口使用代理。