飞牛 NAS 部署 MeshMonitor 完整踩坑记录:从 .env、国内镜像到 Docker 网络

教程

TL;DR

如果你只想先把 MeshMonitor 在飞牛 NAS 上跑起来,可以先记住下面几件事:

  • Compose 写了 env_file: .env,项目目录里就必须真的存在 .env;不想单独维护文件,就删掉 env_file,直接使用 environment
  • 官方镜像是 ghcr.io/yeraze/meshmonitor:latest。国内网络拉取很慢时,可以临时使用可信的 GHCR 镜像代理,但要接受同步延迟和第三方供应链风险。
  • 正常情况下应该优先使用 Docker Bridge,并映射 8080:3001。这也是 MeshMonitor 官方文档推荐的方式。
  • 如果容器内访问 3001 正常、NAS 本机访问映射端口也正常,但同一局域网里的电脑和手机都超时,问题很可能出在 NAS 的 Docker Bridge 入站转发路径,而不是 MeshMonitor 本身。
  • 本文测试的飞牛 fnOS 环境最终通过 network_mode: host 绕过了 Bridge 转发问题。
  • Host 模式下 ports 映射无效,浏览器要访问应用实际监听的端口。本次镜像实际监听 3001,所以最终地址是 http://NAS-IP:3001
  • ALLOWED_ORIGINS 必须与浏览器地址完全一致,包括协议、IP 和端口。
  • 首次登录账号通常是 admin,密码是 changeme,登录后务必立即修改。

本文最终可用的配置如下。请把示例中的两个 IP 换成自己的地址:

services:
  meshmonitor:
    image: ghcr.nju.edu.cn/yeraze/meshmonitor:latest
    container_name: meshmonitor
    network_mode: host
    restart: unless-stopped
    volumes:
      - meshmonitor-data:/data
    environment:
      - TZ=Asia/Shanghai
      - MESHTASTIC_NODE_IP=192.168.0.246
      - ALLOWED_ORIGINS=http://192.168.0.200:3001,http://localhost:3001

volumes:
  meshmonitor-data:
    driver: local

部署完成后访问:

http://192.168.0.200:3001

上面的南京大学镜像地址是第三方代理示例,并非 MeshMonitor 官方镜像仓库。如果你的网络可以正常访问 GHCR,优先换回 ghcr.io/yeraze/meshmonitor:latest

这次部署的环境

这次排障使用的环境如下:

  • NAS:飞牛 fnOS,排障时版本为 1.2.0203
  • NAS 局域网 IP:192.168.0.200
  • Meshtastic 节点 IP:192.168.0.246
  • Meshtastic TCP 端口:4403
  • MeshMonitor:Docker Compose 部署
  • 目标:让局域网中的电脑和手机都能打开 MeshMonitor

IP 地址只是示例。你的 NAS 可能是 192.168.1.10,节点可能是 192.168.1.20,替换时不要照抄。

第一坑:env file .../.env not found

最开始遇到的报错是:

env file /vol1/1000/Docker/meshmonitor/.env not found:
stat /vol1/1000/Docker/meshmonitor/.env: no such file or directory

原因很直接:Compose 中写了下面这一行:

env_file: .env

这等于告诉 Docker Compose:“请到 Compose 文件所在目录读取 .env。”但对应目录里并没有这个文件,Compose 在创建容器前就会停止。

有两种正确处理方式。

方案 A:不用单独的 .env

删除:

env_file: .env

把配置直接写在:

environment:
  - TZ=Asia/Shanghai
  - MESHTASTIC_NODE_IP=192.168.0.246

这种方式对刚接触飞牛 Docker 的用户更直观,所有配置都在同一个页面里。

方案 B:真的创建 .env

如果你使用 MeshMonitor 官方的 Compose 配置生成器,或者希望把变量单独存放,就在报错指向的项目目录中创建 .env

TZ=Asia/Shanghai
MESHTASTIC_NODE_IP=192.168.0.246
ALLOWED_ORIGINS=http://192.168.0.200:8080

然后在 Compose 中保留:

env_file: .env

不要只在 Compose 编辑器里写 env_file: .env,却忘了在文件管理器中创建文件。

第二坑:YAML 缩进看起来简单,实际上很严格

Compose 是 YAML。portsvolumesenvironment 下面的列表必须继续缩进:

services:
  meshmonitor:
    volumes:
      - meshmonitor-data:/data
    environment:
      - TZ=Asia/Shanghai

下面这种写法是错误的:

services:
  meshmonitor:
    volumes:
- meshmonitor-data:/data

飞牛的编辑器有时会让缩进不够显眼。遇到“mapping”“sequence”或“解析 YAML”一类报错时,先检查空格,不要急着怀疑镜像。

第三坑:GHCR 在国内拉取很慢

MeshMonitor 官方镜像地址是:

image: ghcr.io/yeraze/meshmonitor:latest

国内网络访问 GitHub Container Registry 时,可能出现速度很慢、长时间卡住或超时。本文测试时改用南京大学的 GHCR 代理后,镜像很快就拉取完成:

image: ghcr.nju.edu.cn/yeraze/meshmonitor:latest

这里要耐心区分“镜像代理”和“镜像本身”:

  • yeraze/meshmonitor 仍然是原项目路径;
  • 前面的 Registry 域名变成了第三方代理;
  • 代理可能比官方仓库晚同步;
  • 第三方代理的可用性和可信度需要自行评估;
  • 遇到版本异常时,应该切回官方 GHCR 对照验证。

如果准备长期运行,还可以把 latest 换成明确版本号,避免一次更新同时引入应用变化和部署变化。

先从官方推荐的 Bridge 配置开始

不是每台飞牛 NAS 都会遇到本文后面的网络问题。正常情况下,应该先使用 MeshMonitor 官方推荐的 Bridge 方案:

services:
  meshmonitor:
    image: ghcr.io/yeraze/meshmonitor:latest
    container_name: meshmonitor
    ports:
      - "8080:3001"
    restart: unless-stopped
    volumes:
      - meshmonitor-data:/data
    environment:
      - TZ=Asia/Shanghai
      - MESHTASTIC_NODE_IP=192.168.0.246
      - ALLOWED_ORIGINS=http://192.168.0.200:8080,http://localhost:8080

volumes:
  meshmonitor-data:
    driver: local

这里的含义是:

  • MeshMonitor 在容器内部监听 3001
  • Docker 将 NAS 的 8080 转发到容器的 3001
  • 浏览器访问 http://192.168.0.200:8080
  • 数据保存在 Docker 卷 meshmonitor-data 中,重建容器不会自动清空数据。

Docker 官方文档说明,Bridge 容器的端口默认不能直接从宿主机外访问;配置 8080:3001 后,Docker 会创建对应的端口发布和防火墙规则。

如果这一配置能直接打开页面,就不需要改成 Host 模式。Bridge 的网络隔离更清晰,也更容易只暴露需要的端口。

第四坑:容器显示“正在运行”,不等于局域网一定能打开

这次最费时间的地方,是飞牛界面显示容器正在运行,但浏览器访问:

http://192.168.0.200:8080

一直超时。

这时候不要反复删除、重装容器。应该把问题拆成四层:

  1. 应用进程有没有启动;
  2. 容器内部有没有监听端口;
  3. Docker 有没有建立端口映射;
  4. 局域网流量能不能穿过 NAS 防火墙和 Docker 转发链。

检查 Docker 端口映射

sudo docker inspect meshmonitor --format '{{json .NetworkSettings.Ports}}'

正常的 Bridge 输出类似:

{
  "3001/tcp": [
    {"HostIp":"0.0.0.0","HostPort":"8080"},
    {"HostIp":"::","HostPort":"8080"}
  ]
}

这只能证明 Docker 记录了映射,不能单独证明远端访问已经成功。

检查容器内部监听端口

MeshMonitor 镜像中可能没有 ss,可以用 netstat

sudo docker exec meshmonitor sh -lc 'ss -lntp || netstat -lntp'

本次看到的关键输出是:

tcp  0  0  :::3001  :::*  LISTEN

这说明 MeshMonitor Web 服务已经启动,并监听所有地址的 3001

从容器内部请求应用

sudo docker exec meshmonitor sh -lc \
  'wget -S -O /dev/null http://127.0.0.1:3001 2>&1'

如果返回 HTTP/1.1 200 OK,应用本身是健康的。

从 NAS 本机请求发布端口

curl -I --max-time 5 http://127.0.0.1:8080
curl -I --max-time 5 http://192.168.0.200:8080

本次两条命令都返回了 200 OK,说明:

  • MeshMonitor 正常;
  • 容器内部端口正常;
  • Docker 的本机端口发布正常;
  • NAS 自己通过局域网 IP 访问也正常。

但同一局域网中的 Mac 和手机仍然超时。这一步非常关键,因为它排除了浏览器缓存、Mac 代理和单一客户端故障。

检查 Docker NAT 和转发规则

sudo iptables -t nat -S DOCKER | grep -E '8080|4404'
sudo iptables -nvL FORWARD --line-numbers
sudo iptables -nvL DOCKER-USER --line-numbers
sudo iptables -nvL DOCKER --line-numbers

本次能够看到 8080 被 DNAT 到 172.18.0.2:3001DOCKER 链也确实有数据包命中 3001 的 ACCEPT 规则。

换句话说,请求已经到达 Docker 转发规则,但局域网客户端没有完成连接。即使继续在飞牛防火墙中放行 808030014404,问题仍然存在。

这里需要谨慎描述:我们确认的是这台设备、这个 fnOS 版本上的实际现象,并没有据此断言所有飞牛 NAS 都存在同一个缺陷。由于系统没有预装 tcpdump,本次没有继续追到最终丢包位置。

第五坑:误以为是 Mac、Clash 或 Tailscale

排障过程中,Mac 同时运行过 Clash Verge 和 Tailscale,因此很容易先怀疑代理或路由冲突。

我们依次做了以下测试:

  • 关闭 Mac 上的 Clash Verge;
  • 关闭 Mac 上的 Tailscale;
  • 关闭 NAS 上的 Tailscale;
  • 强制从 Mac 的局域网地址访问;
  • 换同一 Wi-Fi 下的手机访问。

结果电脑和手机都无法访问,而 NAS 本机可以返回 200。因此,这次问题不在某一台客户端,也不应该继续围绕浏览器插件或系统代理打转。

排障时最好遵循这个顺序:先换设备,再关代理,最后才修改 Docker 网络。这样可以避免因为一个偶然现象,把 Compose 改得越来越复杂。

最终方案:使用 Host 网络绕过 Bridge 转发

最终使用:

network_mode: host

Host 模式下,容器共享 NAS 的网络命名空间,不再拥有独立的 Bridge IP,也不再经过 8080:3001 这种 NAT 端口映射。

Docker 官方文档明确说明:Host 模式下 ports-p--publish 都会被忽略。因此 Compose 中必须删除整个 ports 部分。

最终配置为:

services:
  meshmonitor:
    image: ghcr.nju.edu.cn/yeraze/meshmonitor:latest
    container_name: meshmonitor
    network_mode: host
    restart: unless-stopped
    volumes:
      - meshmonitor-data:/data
    environment:
      - TZ=Asia/Shanghai
      - MESHTASTIC_NODE_IP=192.168.0.246
      - ALLOWED_ORIGINS=http://192.168.0.200:3001,http://localhost:3001

volumes:
  meshmonitor-data:
    driver: local

飞牛防火墙至少需要允许可信局域网访问 TCP 3001。保存配置、重新创建容器后,可以检查:

sudo docker exec meshmonitor sh -lc 'netstat -lntp'

本次部署实际看到:

tcp  0  0  :::3001  :::*  LISTEN

于是正确访问地址是:

http://192.168.0.200:3001

电脑和手机此时都能正常打开页面。

第六坑:设置了 PORT=8080,实际仍监听 3001

MeshMonitor 配置文档把 PORT 列为可选环境变量,默认值是 3001。但在这次使用的镜像和部署环境中,即使写了:

- PORT=8080

netstat 仍显示服务监听 3001,访问 NAS 的 8080 会立即得到“连接被拒绝”。

这里不要凭配置猜端口,要以容器中的实际监听结果为准:

sudo docker exec meshmonitor sh -lc 'netstat -lntp'

为了减少歧义,最终配置删除了无效的 PORT=8080,直接使用应用实际监听的 3001

如果你一定要保留 http://NAS-IP:8080,可以在飞牛中配置反向代理:

192.168.0.200:8080  →  127.0.0.1:3001

此时还要把 http://192.168.0.200:8080 加入 ALLOWED_ORIGINS。如果只是家庭局域网使用,直接访问 3001 更简单。

第七坑:ALLOWED_ORIGINS 不是随便填一个地址

MeshMonitor 会检查浏览器请求来源。下面三个来源彼此不同:

http://localhost:3001
http://192.168.0.200:3001
http://192.168.0.200:8080

协议、主机名或端口只要有一个不同,就属于不同 Origin。

局域网通过 NAS IP 和 3001 访问时,应写:

- ALLOWED_ORIGINS=http://192.168.0.200:3001,http://localhost:3001

不要为了省事长期使用:

- ALLOWED_ORIGINS=*

通配符适合短时间定位 CORS 问题,不适合长期部署。

如果页面能打开,但登录、接口请求或 WebSocket 异常,也要重新检查这里,而不是只检查端口。

检查 MeshMonitor 能否连接 Meshtastic 节点

Web 页面能打开,和 MeshMonitor 能否连接节点,是两个独立问题。

首先在 NAS 上检查节点的 TCP 端口:

nc -vz 192.168.0.246 4403

再从容器网络中检查:

sudo docker exec meshmonitor node -e \
  'const n=require("net");const s=n.connect(4403,"192.168.0.246",()=>{console.log("4403 connected");s.end()});s.on("error",console.error)'

看到:

4403 connected

只能证明 TCP 端口可达。如果日志中仍反复出现 connecteddisconnectedNot connected to Meshtastic node,继续检查:

  • 节点 Wi-Fi 或以太网是否稳定;
  • 节点的 TCP 服务是否正常;
  • 是否有手机、电脑上的其他客户端同时连接并争用设备;
  • MeshMonitor 后台 Dashboard → Sources 中保存的 IP 和端口是否正确。

MeshMonitor 4.0 之后,MESHTASTIC_NODE_IPMESHTASTIC_TCP_PORT 主要用于第一次启动时创建初始 Source。数据卷中已经有配置后,再修改环境变量不一定会覆盖现有 Source,应该进入 Dashboard → Sources 修改。

查看实时日志可以使用:

sudo docker logs -f meshmonitor

关于 4404:不要和节点的 4403 混淆

Meshtastic 节点通常在 TCP 4403 等待 MeshMonitor 连接。数据方向是:

MeshMonitor  →  Meshtastic 节点 IP:4403

4404 则通常与 MeshMonitor 的 Virtual Node 功能有关。启用后,手机端可以把 MeshMonitor 当成一个 TCP 节点连接。没有启用 Virtual Node 时,不必因为官方示例或旧配置中出现过 4404,就认定它一定应该处于监听状态。

Host 模式的代价和安全注意事项

Host 模式解决了这次飞牛 Bridge 转发异常,但它不是没有代价。

1. 网络隔离更弱

容器直接共享 NAS 网络。容器里所有监听 0.0.0.0:: 的服务,都可能直接出现在 NAS 的端口上。

本次使用 netstat 时,除了 MeshMonitor 的 3001,还观察到内部 Python/Apprise 服务监听 8000。请根据实际功能检查端口,不要只关注 3001

sudo docker exec meshmonitor sh -lc 'netstat -lntp'

防火墙来源 IP 最好限制为可信局域网,而不是无条件允许所有来源。

2. 端口冲突会直接导致启动失败

如果 NAS 上已经有其他服务占用 30018000,Host 模式下 MeshMonitor 无法再次绑定同一端口。部署前可以检查:

sudo netstat -lntp | grep -E ':3001|:8000'

3. 不要直接暴露到互联网

家庭局域网部署可以使用 HTTP,但不要直接在路由器上把 3001 映射到公网。需要公网访问时,优先使用 VPN,或者按照 MeshMonitor 官方生产部署说明配置 HTTPS 反向代理、强随机 SESSION_SECRETTRUST_PROXY=trueCOOKIE_SECURE=true,并把 ALLOWED_ORIGINS 精确设置为 HTTPS 域名。

4. 立即修改默认密码

首次启动通常会创建:

用户名:admin
密码:changeme

页面打开后第一件事不是研究地图,而是修改管理员密码。

数据备份与更新

本文配置使用命名卷:

volumes:
  - meshmonitor-data:/data

这意味着重建容器时数据仍保留,但删除 Docker 卷会丢失数据库。更新前可以先检查卷并备份数据。

常规更新命令是:

docker compose pull
docker compose up -d

如果使用国内镜像代理,更新后发现版本没有变化,可能是代理尚未同步。此时切回官方 GHCR 拉取,或者使用明确版本号核对。

更新完成后建议重新检查:

sudo docker logs --tail 100 meshmonitor
sudo docker exec meshmonitor sh -lc 'netstat -lntp'

一份可以照着走的排障清单

以后再遇到“容器在运行,但页面打不开”,可以按下面顺序检查:

  1. Compose 是否因为 .env 不存在或 YAML 缩进错误而根本没有创建容器;
  2. docker ps 中容器是否持续运行,而不是反复重启;
  3. docker logs --tail 200 meshmonitor 是否有启动错误;
  4. 容器内是否真的监听 3001
  5. 容器内请求 127.0.0.1:3001 是否返回 200;
  6. Bridge 模式下,docker inspect 是否存在 8080:3001 映射;
  7. NAS 本机访问 127.0.0.1:8080 是否成功;
  8. NAS 本机访问 NAS-IP:8080 是否成功;
  9. 同一局域网中的另一台电脑和手机是否都失败;
  10. 飞牛防火墙是否放行了实际访问端口;
  11. Bridge 转发仍异常时,再考虑 Host 模式;
  12. Host 模式下删除 ports,以 netstat 看到的实际端口为准;
  13. 把实际浏览器地址完整写入 ALLOWED_ORIGINS
  14. 页面打开后,再单独排查 Meshtastic 节点的 4403 连接。

这套顺序的重点是:每一步只验证一层。不要在还没确认应用是否监听时就反复修改防火墙,也不要因为浏览器打不开,就立即断定镜像启动失败。

结语

这次折腾最容易误导人的地方,是每一层看起来都“差一点就正常”:镜像终于下载好了,容器显示正在运行,应用返回 200,Docker 的 DNAT 规则存在,防火墙规则也生效,但局域网设备就是打不开。

真正有用的不是记住某一条神奇命令,而是把路径拆开:

浏览器 → NAS 防火墙 → Docker 端口发布 → 容器监听 → MeshMonitor → Meshtastic 节点:4403

本文环境最终通过 Host 网络绕开了 Bridge 转发问题,并以应用实际监听的 3001 对外提供服务。其他飞牛用户如果 Bridge 配置可以正常工作,继续使用官方推荐方案即可;只有在证据表明“容器和 NAS 本机都正常,但所有局域网客户端都失败”时,才值得切换 Host 模式。

相关资料:

本文作者: Hays Chan | 陈希

本文链接: https://meshcn.net/meshmonitor-fnos-docker-troubleshooting/