How to Deploy Motrix as a Headless Docker Server with Device-Code Pairing

Motrix ships an official multi-architecture Docker image that exposes a Web UI on port 8080 and an MDXP pairing service on port 16801, enabling you to run a fully headless server that authenticates remote clients via a browser-based device-code approval flow.

The agalwood/Motrix repository provides a production-ready server container that eliminates the need for a desktop environment. When you deploy Motrix as a headless Docker server with device-code pairing, you enable the @motrix/cli client to securely exchange credentials through the MDXP (Motrix Download eXchange Protocol) JSON-RPC endpoint.

Architecture and Network Requirements

The container runtime defined in the Dockerfile exposes three core services documented in docs/docker-server.md:

  • Port 8080: Serves the Web UI dashboard and the HTTP API consumed by browser clients and CLI agents.
  • Port 16801: Exposes the MDXP JSON-RPC 2.0 endpoint dedicated exclusively to device-code pairing and remote client initialization.
  • Health endpoint: The /healthz route on port 8080 supports Docker’s built-in health checks to verify container readiness.

Persistent state—including the SQLite database, aria2 session files, plugins, and the operator token—resides in /data. Downloaded files are written to /downloads. The image executes as the non-root node user with UID/GID 1000, requiring both mount points to be writable by this identity.

Preparing Persistent Storage

Before starting the container, create host directories and align ownership with the container’s user to prevent permission errors when the server writes the operator token to /data/operator-token.

mkdir -p motrix-data downloads
export MOTRIX_UID="$(id -u)"
export MOTRIX_GID="$(id -g)"
chown "$MOTRIX_UID:$MOTRIX_GID" motrix-data downloads

Essential Environment Variables

The pairing flow relies on the MOTRIX_PUBLIC_URL variable to generate valid approval URLs that clients can reach.

Set MOTRIX_PUBLIC_URL to the externally reachable address of your Web UI (e.g., http://nas.example.lan:8080). This value must resolve from the client machine; localhost addresses will break the browser redirect during pairing. Additionally, bind the MDXP service to all interfaces by setting MOTRIX_MDXP_HOST=0.0.0.0 so remote agents can connect to port 16801.

Deployment Options

You can launch the server using Docker Compose configurations included in the repository or a standalone docker run command.

Docker Compose (Bind Mount)

The compose.yaml file at the repository root mounts host directories directly into the container.

export MOTRIX_PUBLIC_URL='http://nas.example.lan:8080'
docker compose pull server
docker compose up -d --wait
docker compose ps

Docker Compose (Named Volumes)

For NAS environments or scenarios preferring Docker-managed storage, use compose.named-volumes.yaml:

export MOTRIX_PUBLIC_URL='http://nas.example.lan:8080'
docker compose -f compose.named-volumes.yaml pull server
docker compose -f compose.named-volumes.yaml up -d --wait

Standalone Docker Run

For custom orchestration, run the container directly with security-hardened flags:

docker run -d \
  --name motrix-server \
  --init \
  --restart unless-stopped \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 \
  --security-opt no-new-privileges:true \
  --user "$MOTRIX_UID:$MOTRIX_GID" \
  -e MOTRIX_PUBLIC_URL='http://nas.example.lan:8080' \
  -e MOTRIX_MDXP_HOST=0.0.0.0 \
  -p 8080:8080 \
  -p 16801:16801 \
  -v "$(pwd)/motrix-data:/data" \
  -v "$(pwd)/downloads:/downloads" \
  motrixapp/motrix-server:latest

Device-Code Pairing Workflow

Once the server is healthy, authenticate remote clients using the three-step protocol implemented in dist/server/motrix-admin.mjs and the @motrix/cli package.

1. Initiate pairing on the client:

motrix pair --name my-nas

The CLI contacts the MDXP endpoint at port 16801 and displays a short device code (e.g., ABCD-EFGH).

2. Approve via browser:

Open the URL specified in MOTRIX_PUBLIC_URL in any browser, log in using the operator token found at motrix-data/operator-token, and submit the device code shown in the terminal.

3. Automatic token issuance:

Upon approval, the server returns a long-lived authentication token that the CLI stores locally for subsequent API calls to port 8080. The client can now execute commands like motrix add, motrix list, and motrix watch --stats.

CLI-Based Approval

If you have SSH access to the Docker host, you can approve pending requests directly via motrix-admin without opening a browser:

docker compose exec server motrix-admin pairing pending
docker compose exec server motrix-admin pairing approve ABCD-EFGH

Deny suspicious requests using:

docker compose exec server motrix-admin pairing deny ABCD-EFGH

Health Checks and Diagnostics

Verify container readiness using the built-in health endpoint:

curl --fail http://127.0.0.1:8080/healthz

Retrieve server diagnostics by authenticating with the operator token:

TOKEN="$(cat motrix-data/operator-token)"
curl --fail --header "Authorization: Bearer ${TOKEN}" http://127.0.0.1:8080/api/diagnostics

Security Considerations

By default, MDXP and Web traffic travels over unencrypted HTTP, which is acceptable for trusted LANs. The pairing mechanism itself does not enforce HTTPS, so for Internet-facing deployments you should terminate TLS at a reverse proxy and restrict origin ports appropriately. The container runs with --read-only and --security-opt no-new-privileges:true to minimize attack surface, storing mutable state only in the mounted /data and /downloads volumes.

Summary

  • Deploy the agalwood/Motrix server image using compose.yaml or docker run, exposing ports 8080 and 16801.
  • Mount host directories to /data and /downloads, ensuring ownership matches UID/GID 1000.
  • Set MOTRIX_PUBLIC_URL to a non-localhost address so clients can complete browser-based pairing.
  • Execute motrix pair --name <device> on remote machines, then approve codes via the Web UI or motrix-admin pairing approve.
  • Retrieve the operator token from motrix-data/operator-token to administer the server or query diagnostic endpoints.

Frequently Asked Questions

Where is the operator token stored after the first start?

The server generates the operator token at /data/operator-token inside the container, which maps to motrix-data/operator-token on your host when using bind mounts. Inspect it with cat motrix-data/operator-token and use this value to log into the Web UI or authorize diagnostic API requests.

Can I run the container as root or with a different user ID?

The official image is designed to run as the node user (1000:1000). While you can override this with --user, doing so may cause permission mismatches with pre-existing files in /data. It is safer to adjust host directory ownership to match UID 1000 rather than changing the container user.

Why does my client show a device code but the Web UI never sees the request?

This usually indicates that MOTRIX_PUBLIC_URL is set to http://localhost:8080 or an internal Docker network address that the browser cannot resolve. The MDXP service on port 16801 issues the code, but the browser must reach the Web UI on port 8080 at the exact URL defined in MOTRIX_PUBLIC_URL to complete the OAuth-style approval flow.

Is HTTPS required for device-code pairing to work?

No. The pairing protocol functions over plain HTTP, making it suitable for local networks. However, if exposing port 8080 or 16801 to the Internet, you should place a reverse proxy handling TLS termination in front of the container to encrypt traffic and protect the operator token and authentication flows.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →