NAS 上跑 Motrix Server:无头部署、三端口与 /data 备份边界
项目信息
- 仓库:agalwood/Motrix
- 官网:motrix.app
- Docker 部署文档:motrix.app/zh/manual/docker/
- 文档源文件:docs/docker-server.zh-CN.md
Motrix v2(Motrix Turbo)使用 Electron + React + TypeScript 重写,下载内核与 UI 分离。浏览器扩展和命令行通过 MDXP(基于 JSON-RPC 2.0 的开放协议)与内核通信,插件跑在隔离沙箱。同一套内核有两种形态:桌面版(macOS/Windows/Linux)和无头服务器版。服务器版直接跑 Node.js 或 Docker,带 Web UI,面向 NAS 和家用服务器;它不包含桌面端的启动项、通知偏好、NAT 控件。NAS 通常没有桌面环境,也不需要这些控件,用无头模式配合浏览器访问 8080 更直接。
镜像不安装发行版通用 aria2,而是打包经过 checksum 校验的 motrixapp/aria2 fork,提供 SQLite 持久化契约。
镜像与 tag
同一镜像推到 Docker Hub 与 GHCR:
docker.io/motrixapp/motrix-serverghcr.io/agalwood/motrix-server
每个发布镜像含 linux/amd64 与 linux/arm64,不支持 32 位 ARM。
| tag | 语义 |
|---|---|
:2.3.4 | 不可变版本 |
:2.3 | 该系列最新补丁 |
:2 | 主版本最新 |
:stable | stable 通道 |
:latest | 与 :stable 同 digest |
:2.3.4-beta.2 | 预发布,不会推动 floating tag |
项目不发 edge/nightly。两个 registry 的 manifest digest 一致后才推进 floating tag。需要固定时可用 @sha256: digest。
三个端口不要混用
| 端口 | 用途 |
|---|---|
8080 | Web UI、operator API、公开 GET /healthz |
16801 | MDXP endpoint:POST /mdxp 单次调用、GET /mdxp/events 事件流、CLI/agent 的 device-code 配对,以及显式开启后的浏览器扩展 MBP1 路由 |
16800 | 原始 aria2 RPC,默认只在容器 loopback 监听,不发布 |
审批界面在 8080,MDXP 在 16801,aria2 RPC 是另一条通道。只转发 8080 不会发布 MDXP,只转发 16801 不提供审批界面。
容器加固
Compose 里保留这些约束:
init: trueread_only: true,根文件系统只读user: 1000:1000no-new-privileges/tmp用noexec,nosuid的 64m tmpfsstop_grace_period: 2mrestart: unless-stopped- 镜像自带 healthcheck,并处理优雅 SIGTERM
不要为了绕挂载权限把 UID 设成 0 或开 privileged,应修正目录 owner。
/data 与 /downloads 的备份边界
/data 存 SQLite 数据库、设置、aria2 session 与 DHT 状态、种子元数据、operator token、插件包/状态/日志、插件 secret lockbox,必须备份。/downloads 存下载文件,按自己的策略备份。
启动时会实际写入测试 /data/tmp、种子、home、插件、下载目录,权限或挂载错误会带绝对路径启动失败;建任务时还会再检查保存目录。MOTRIX_ALLOWED_SAVE_DIRS 是冒号分隔的绝对根路径,canonical path 检查会拒绝路径穿越和符号链接越界。要加 /archive 归档目录,必须同时挂载并在 MOTRIX_ALLOWED_SAVE_DIRS 里声明。
首次登录:operator token
首次启动生成 /data/operator-token,权限 0600,重启和换镜像都复用。
bind mount 时直接读:
cat motrix-data/operator-token
zsh 输出末尾的 % 是终端标记,不要粘进去。named volume 时:
docker compose exec server sh -c 'cat /data/operator-token'
也可以设 MOTRIX_OPERATOR_TOKEN,但环境变量在容器元数据里可见,单机默认用文件更安全。浏览器打开 8080,在 Unlock Motrix 页面粘贴 token。
MOTRIX_PUBLIC_URL 与配对审批
MOTRIX_PUBLIC_URL 是 device-code 客户端拿到的外部可访问 Web 审批 URL,没有 localhost 默认值。要填其他机器真正能用的地址,用 Web 端口或反代 URL,绝不能用 MDXP 端口,也不要填 localhost、127.0.0.1、0.0.0.0。留空不禁用配对,但客户端拿不到可用链接。
配对审批有两条路:
- Web UI 的 Settings → Integration → Command-line tools 里点 Approve。
- SSH 进容器用
motrix-admin:
docker compose exec server motrix-admin pairing pending
docker compose exec server motrix-admin pairing approve ABCD-EFGH
docker compose exec server motrix-admin pairing deny ABCD-EFGH
motrix-admin 只走容器 loopback,不提供 approve-latest / approve-all。不能靠禁用配对来做保护。
aria2 RPC 默认关闭的取舍
只有可信外部客户端要直连引擎时,才设 MOTRIX_ARIA2_RPC_LISTEN_ALL=true,且必须先在 设置→高级→RPC 设非空 RPC secret,否则服务器拒绝在所有接口监听。
Compose 示例故意不发布 16800,要用 override:
services:
server:
ports:
- "127.0.0.1:16800:16800"
export MOTRIX_ARIA2_RPC_LISTEN_ALL=true
docker compose -f compose.yaml -f compose.aria2-rpc.yaml up -d --wait
该 RPC 不受 operator 鉴权和下载路径策略约束,也没有 TLS。持有 secret 基本等于拿到下载引擎控制权,不能直接暴露公网。
网络与 TLS
容器内 listener 与宿主发布端口是两层边界。MOTRIX_MDXP_HOST=0.0.0.0 是容器内监听,宿主绑哪个地址由 ports 决定,相关变量有 MOTRIX_WEB_BIND_IP、MOTRIX_MDXP_BIND_IP、MOTRIX_BIND_IP。
公网访问必须反代终止 TLS,并用防火墙保护 origin 端口,两项都要。远程浏览器扩展需要 MOTRIX_REMOTE_EXTENSION_ENABLED=true 和 MBP1 路由。旧 beta Server 的 URL token / aria2 RPC token 不能复用;旧 Server 在 /discovery、/nonce 返回 404,改端口修不好。
部署命令
bind mount 方式:
mkdir -p motrix-data downloads
export MOTRIX_UID="$(id -u)"; export MOTRIX_GID="$(id -g)"
chown "$MOTRIX_UID:$MOTRIX_GID" motrix-data downloads
export MOTRIX_PUBLIC_URL='http://nas.example.lan:8080'
docker compose pull server && docker compose up -d --wait && docker compose ps
named volume 用 compose.named-volumes.yaml。升级同样是拉新镜像后 up -d --wait,operator token 会继续复用。docker compose down 不要带 --volumes,否则会删卷;named volume 场景尤其要确认这一点。
诊断
curl --fail http://127.0.0.1:8080/healthz
docker compose logs --tail=200 server
/healthz 在 HTTP 与 aria2 都就绪时才成功。带 Bearer token 请求 /api/diagnostics,会返回引擎状态、生效存储路径、允许根目录、runtime UID、插件安装/secret-store 状态、FFmpeg 探测结果。