Matrix Synapse 接入 LiveKit:Cloudflare Worker 与 NPM 部署方案

整理一套经过实际生产验证的 MatrixRTC / LiveKit / JWT / coturn 部署路线:Cloudflare Worker 只承载标准 443 的 well-known,Nginx Proxy Manager 通过自定义 HTTPS 端口反代 Synapse、JWT、LiveKit 与 ntfy。

这篇是 Matrix Synapse 接入 LiveKit 的另一套部署路线:不使用 Cloudflare Tunnel 承载 JWT / LiveKit 的控制链路,而是让 Cloudflare Worker 只负责标准 HTTPS 443 上的 /.well-known/matrix/client/.well-known/matrix/server,真正的 Synapse、matrix-livekit-jwt-service、LiveKit server 和 ntfy 仍然交给 Nginx Proxy Manager(NPM)反代。

如果你在中国大陆地区使用 Cloudflare Tunnel 版时遇到呼叫界面白屏、发起呼叫慢、等待 8 秒以上才弹出呼叫界面,但 coturn 传统通话正常,那么问题通常不在 coturn,而是在 MatrixRTC 这条新链路里的 JWT / LiveKit HTTPS 与 WebSocket 控制路径。只要你的网络环境允许转发自定义端口,本文这种 Worker + NPM 方案通常更稳。

本文按一次真实生产迁移后的配置重新整理,重点是:

  • NPM 配置全部通过 NPM UI 管理,后台能看到,不使用隐藏的手写 nginx 配置。
  • Cloudflare Worker 只接管 666666.xyz/.well-known/matrix/*
  • NPM 用 12335123361233712338 暴露实际服务。
  • LiveKit 媒体走真实 UDP 端口,不走 NPM,不走 Worker,不走 Cloudflare HTTP 代理。
  • 示例域名统一写成 666666.xyz,实际部署时换成自己的域名。

一、最终结构

本文示例使用下面的域名和端口:

Matrix server_name: 666666.xyz
Docker 主机内网 IP: 10.20.20.8

Worker:
  https://666666.xyz/.well-known/matrix/client
  https://666666.xyz/.well-known/matrix/server

NPM HTTPS:
  https://matrix.666666.xyz:12335      -> Synapse 8008
  https://matrix-jwt.666666.xyz:12336  -> JWT service 8170
  https://livekit.666666.xyz:12337     -> LiveKit 7880
  https://sms.666666.xyz:12338         -> ntfy 80

LiveKit RTC:
  50201-50400/udp
  17882/tcp 可选,TCP fallback

coturn:
  coturncoturn7829.666666.xyz
  3478/tcp
  3478/udp
  50000-50200/udp

实际部署时,把域名、IP、密钥都换成自己的。LiveKit secret、TURN shared secret、Cloudflare API token 不要写进公开文章、截图或工单。

二、生产日志策略

生产环境不要完全关闭日志,否则排障会很被动;但一定要限制 Docker 日志体积,避免 json-file 日志无限增长撑爆硬盘。本文所有 docker-compose.yaml 都建议给每个服务加上:

logging:
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"

这样单个容器最多保留约 30MB Docker 日志。服务多一点也不会失控。

应用自身日志建议这样处理:

  • coturn:生产环境不要打开 verbose
  • ntfy:生产环境用 log-level: warn,排障时再临时改成 info
  • Synapse、NPM、LiveKit、JWT:保留默认日志级别即可,靠 Docker 日志轮转限制体积。
  • 排障时临时执行 docker logs --since=5m 容器名,不要长期挂着额外日志采集或 debug 模式。

如果宿主机已经配置了全局 Docker 日志轮转,也可以不在每个 compose 里重复写。但教程里显式写上更稳,照着部署不容易漏。

三、这套方案解决什么问题

Matrix 客户端登录 @user:666666.xyz 时,会优先访问:

https://666666.xyz/.well-known/matrix/client

这个地址必须是标准 HTTPS 443。家宽、内网穿透、旁路由或多服务共存环境里,经常没有办法把本机 443/tcp 直接给 Matrix 用。Cloudflare Worker 可以在 Cloudflare 边缘直接返回两个 well-known JSON:

/.well-known/matrix/client
/.well-known/matrix/server

这样客户端能在标准 443 上发现真实后端,而真实后端继续跑在 NPM 暴露的非标准 HTTPS 端口上。

这套方案的边界要记清楚:

  • Worker 只负责 .well-known 公告。
  • NPM 负责 HTTPS 反代 Synapse、JWT、LiveKit、ntfy。
  • LiveKit UDP 媒体端口必须真实放行到 Docker 主机。
  • coturn 仍然直接暴露 TURN 端口,不走 NPM,也不走 Cloudflare 代理。
  • server_name 仍然是 666666.xyz,Matrix ID 仍然是 @user:666666.xyz

四、DNS 与 Cloudflare 状态

推荐 DNS 这样分配:

记录 类型和值 用途 Cloudflare 状态
666666.xyz AAAA 100:: 只给 Worker 路由接管 .well-known 橙云
matrix.666666.xyz 指向公网 IP / DDNS :12335 访问 Synapse DNS only
matrix-jwt.666666.xyz 指向公网 IP / DDNS :12336 访问 JWT DNS only
livekit.666666.xyz 指向公网 IP / DDNS :12337 访问 LiveKit DNS only
sms.666666.xyz 指向公网 IP / DDNS :12338 访问 ntfy DNS only
coturncoturn7829.666666.xyz 指向公网 IP / DDNS TURN DNS only

根域名 666666.xyz 这里只用于 Worker 路由,不直接访问源站。Cloudflare DNS 里给根域加一条:

AAAA  @  100::  已代理

不要把根域橙云记录指向 Cloudflare 自己的 IP,也不要用 1.1.1.1 这种地址凑数;这会触发 Cloudflare Error 1034: Edge IP Restricted192.0.2.1 属于文档保留地址,也容易被 Cloudflare 拦住。实践里用 AAAA 100:: 做 Worker 路由占位最稳。

matrix.666666.xyzmatrix-jwt.666666.xyzlivekit.666666.xyzsms.666666.xyz 这些自定义端口入口不要开橙云。Cloudflare 代理只支持指定的一批 HTTP/HTTPS 端口,12335123361233712338 不适合走橙云代理。

五、NPM 容器

如果已经有 NPM,可以复用现有容器,只要确认它加入了 matrix Docker 网络,并且宿主机映射了本文需要的几个 HTTPS 端口。

创建目录:

mkdir -p /root/data/docker_data/npm
cd /root/data/docker_data/npm

创建外部 Docker 网络:

docker network create matrix

如果已经存在,会提示重复,忽略即可。

创建 /root/data/docker_data/npm/docker-compose.yml

services:
  app:
    image: jc21/nginx-proxy-manager:latest
    container_name: nginx-proxy-manager
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    ports:
      - "12332:80"
      - "81:81"

      # 12335 -> NPM 容器 443,用于 Synapse
      - "0.0.0.0:12335:443"
      - "[::]:12335:443"

      # 12336 -> NPM 容器 443,用于 JWT
      - "0.0.0.0:12336:443"
      - "[::]:12336:443"

      # 12337 -> NPM 容器 443,用于 LiveKit
      - "0.0.0.0:12337:443"
      - "[::]:12337:443"

      # 12338 -> NPM 容器 443,用于 ntfy
      - "0.0.0.0:12338:443"
      - "[::]:12338:443"
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    networks:
      - matrix

networks:
  matrix:
    external: true
    name: matrix

启动:

docker compose up -d
docker logs -f nginx-proxy-manager

NPM 后台:

http://10.20.20.8:81

六、NPM Proxy Host

进入 NPM 后台后,通过 UI 添加四个 Proxy Host。不要手写 /data/nginx/proxy_host/*.conf 这种隐藏配置,否则能工作但 NPM UI 看不到,后续排障会很痛苦。也不要让隐藏配置和 UI 配置同时使用同一个 server_name,否则容易出现重复监听或配置覆盖。

最终在 NPM UI 里应该能看到类似下面这四条。这里全部用示例域名脱敏:

sms.666666.xyz         -> 10.20.20.8:80
livekit.666666.xyz     -> 10.20.20.8:7880
matrix.666666.xyz      -> matrix-synapse:8008
matrix-jwt.666666.xyz  -> 10.20.20.8:8170

这四条都要在 NPM 的 Proxy Hosts 列表中可见。如果你只是手工写了 nginx 配置文件,但 UI 里没有这些 Proxy Host,后续证书、WebSocket、启停和状态检查都会变得不直观,建议改回 UI 管理方式。

证书建议直接申请或导入通配符证书:

*.666666.xyz

如果没有通配符证书,也可以给每个域名单独申请 Let’s Encrypt 证书。每个 Proxy Host 都建议开启:

Websockets Support: ON
Force SSL: ON
HTTP/2 Support: ON

1. Synapse

项目
Domain Names matrix.666666.xyz
Scheme http
Forward Hostname / IP matrix-synapse
Forward Port 8008
Websockets Support 开启
SSL 通配符证书或 matrix.666666.xyz 证书

Synapse 在 matrix Docker 网络里,所以 NPM 可以通过容器名访问它:

http://matrix-synapse:8008

外部访问地址:

https://matrix.666666.xyz:12335

2. JWT service

项目
Domain Names matrix-jwt.666666.xyz
Scheme http
Forward Hostname / IP 10.20.20.8
Forward Port 8170
Websockets Support 开启
SSL 通配符证书或 matrix-jwt.666666.xyz 证书

JWT 服务使用 network_mode: host 时,NPM 后台不要写容器名,直接写 Docker 主机内网 IP:

http://10.20.20.8:8170

外部访问地址:

https://matrix-jwt.666666.xyz:12336

3. LiveKit

项目
Domain Names livekit.666666.xyz
Scheme http
Forward Hostname / IP 10.20.20.8
Forward Port 7880
Websockets Support 开启
SSL 通配符证书或 livekit.666666.xyz 证书

LiveKit 也使用 network_mode: host,NPM 上游同样写 Docker 主机内网 IP:

http://10.20.20.8:7880

外部访问地址:

https://livekit.666666.xyz:12337

4. ntfy

本文生产配置里 ntfy 也使用 host 网络,因此 NPM 上游写 Docker 主机内网 IP:

项目
Domain Names sms.666666.xyz
Scheme http
Forward Hostname / IP 10.20.20.8
Forward Port 80
Websockets Support 开启
SSL 通配符证书或 sms.666666.xyz 证书

外部访问地址:

https://sms.666666.xyz:12338

如果你的 ntfy 不是 host 网络,而是接入了 matrix Docker 网络,也可以把 NPM 上游写成:

http://ntfy:80

两种写法不要混。host 网络服务用 10.20.20.8:端口;Docker bridge 网络服务才用容器名。

NPM 显示 Offline 怎么排

如果 NPM UI 里某一项显示 Offline,先按这个顺序检查:

docker exec nginx-proxy-manager curl -I http://matrix-synapse:8008/_matrix/client/versions
docker exec nginx-proxy-manager curl -I http://10.20.20.8:8170/healthz
docker exec nginx-proxy-manager curl -I http://10.20.20.8:7880/
docker exec nginx-proxy-manager curl -I http://10.20.20.8:80/

常见原因:

  • Proxy Host 被禁用了。
  • 上游写错,例如 matrix-synapse. 多了一个尾部点。
  • NPM 没有加入 matrix Docker 网络。
  • host 网络服务却在 NPM 里写了容器名。
  • ntfy 实际监听在宿主机 80,但 NPM 写成了 ntfy:80

七、Cloudflare Worker

创建一个 Worker,例如命名为:

matrix-jwt

Worker 代码:

const homeserverUrl = "https://matrix.666666.xyz:12335";
const jwtUrl = "https://matrix-jwt.666666.xyz:12336";
const federationServer = "matrix.666666.xyz:12335";

function json(data) {
  return new Response(JSON.stringify(data), {
    headers: {
      "content-type": "application/json; charset=utf-8",
      "access-control-allow-origin": "*",
      "access-control-allow-methods": "GET, OPTIONS",
      "cache-control": "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0",
      "pragma": "no-cache",
      "expires": "0"
    }
  });
}

export default {
  async fetch(request) {
    const url = new URL(request.url);

    if (request.method === "OPTIONS") {
      return new Response(null, {
        status: 204,
        headers: {
          "access-control-allow-origin": "*",
          "access-control-allow-methods": "GET, OPTIONS"
        }
      });
    }

    if (url.pathname === "/.well-known/matrix/client") {
      return json({
        "m.homeserver": {
          "base_url": homeserverUrl
        },
        "org.matrix.msc4143.rtc_foci": [
          {
            "type": "livekit",
            "livekit_service_url": jwtUrl
          }
        ],
        "io.element.rtc": {
          "livekit": {
            "service_url": jwtUrl
          }
        }
      });
    }

    if (url.pathname === "/.well-known/matrix/server") {
      return json({
        "m.server": federationServer
      });
    }

    return new Response("Not Found", { status: 404 });
  }
};

Worker 预览页面访问根路径返回 Not Found 是正常的,因为代码只处理两个 well-known 路径。应测试:

https://你的-worker.workers.dev/.well-known/matrix/client
https://你的-worker.workers.dev/.well-known/matrix/server

确认 Worker 预览正常后,到 Cloudflare 站点的 Workers 路由里添加:

666666.xyz/.well-known/matrix/*

有些教程会写成访问 URL:

https://666666.xyz/.well-known/matrix/*

这是在说明最终访问地址。Cloudflare 路由输入框通常写 域名/路径,也就是 666666.xyz/.well-known/matrix/*

最终真正要测的是自定义路由,不是 Worker 根路径:

https://666666.xyz/.well-known/matrix/client
https://666666.xyz/.well-known/matrix/server

验证正式域名:

curl -i https://666666.xyz/.well-known/matrix/client
curl -i https://666666.xyz/.well-known/matrix/server

重点看:

HTTP/2 200
content-type: application/json; charset=utf-8
access-control-allow-origin: *

client 应返回:

{
  "m.homeserver": {
    "base_url": "https://matrix.666666.xyz:12335"
  },
  "org.matrix.msc4143.rtc_foci": [
    {
      "type": "livekit",
      "livekit_service_url": "https://matrix-jwt.666666.xyz:12336"
    }
  ],
  "io.element.rtc": {
    "livekit": {
      "service_url": "https://matrix-jwt.666666.xyz:12336"
    }
  }
}

server 应返回:

{
  "m.server": "matrix.666666.xyz:12335"
}

八、Synapse 配置

Synapse 仍然沿用 matrix Docker 网络,NPM 可以通过容器名 matrix-synapse:8008 访问它。

/root/data/docker_data/synapse/docker-compose.yml 示例:

services:
  synapse:
    image: matrixdotorg/synapse:latest
    container_name: matrix-synapse
    volumes:
      - /root/data/docker_data/synapse/data/:/data/
      - /root/data/docker_data/synapse/html/:/usr/local/lib/python3.13/site-packages/synapse/static/
    ports:
      - "8008:8008"
    networks:
      - matrix
    restart: always
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    environment:
      - "TZ=Asia/Shanghai"

networks:
  matrix:
    external: true
    name: matrix

编辑 /root/data/docker_data/synapse/data/homeserver.yaml

基础域名:

server_name: "666666.xyz"
public_baseurl: "https://matrix.666666.xyz:12335"

server_name 不要改成 matrix.666666.xyz。它决定 Matrix 用户 ID,例如:

@alice:666666.xyz

public_baseurl 才是客户端访问 Synapse 的真实 HTTPS 入口。

8008 listener 要同时包含 clientfederation

listeners:
  - port: 8008
    tls: false
    type: http
    x_forwarded: true
    resources:
      - names: [client, federation]
        compress: false

MatrixRTC 配置:

experimental_features:
  msc3266_enabled: true
  msc4222_enabled: true
  msc4140_enabled: true
  msc4143_enabled: true

max_event_delay_duration: 24h

rc_message:
  per_second: 0.5
  burst_count: 30

rc_delayed_event_mgmt:
  per_second: 1
  burst_count: 20

matrix_rtc:
  transports:
    - type: livekit
      livekit_service_url: "https://matrix-jwt.666666.xyz:12336"

coturn 配置继续保留:

turn_uris:
  [
    "turn:coturncoturn7829.666666.xyz?transport=udp",
    "turn:coturncoturn7829.666666.xyz?transport=tcp",
  ]
turn_shared_secret: "替换成你的 TURN shared secret"
turn_user_lifetime: 86400000
turn_allow_guests: True

重启:

cd /root/data/docker_data/synapse
docker compose up -d
docker logs -f matrix-synapse

九、LiveKit server

LiveKit 建议使用 host 网络。NPM 只反代 7880 的 HTTP/WebSocket,LiveKit UDP 媒体端口仍然直接走宿主机。

创建目录:

mkdir -p /root/data/docker_data/livekit-server/etc
cd /root/data/docker_data/livekit-server

创建 /root/data/docker_data/livekit-server/etc/livekit.yaml

port: 7880
bind_addresses:
  - ""

rtc:
  tcp_port: 17882
  port_range_start: 50201
  port_range_end: 50400
  use_external_ip: true
  enable_loopback_candidate: false
  stun_servers:
    - "stun.miwifi.com:3478"
    - "stun.chat.bilibili.com:3478"

keys:
  livekit_api_key_example: livekit_secret_example

创建 /root/data/docker_data/livekit-server/docker-compose.yaml

services:
  livekit-server:
    image: livekit/livekit-server:latest
    container_name: livekit-server
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    network_mode: host
    command: --config /etc/livekit.yaml
    volumes:
      - ./etc/livekit.yaml:/etc/livekit.yaml:ro

启动:

docker compose up -d
docker logs -f livekit-server

NPM 里的 LiveKit Proxy Host 上游指向:

http://10.20.20.8:7880

JWT 和客户端看到的 LiveKit 地址是:

wss://livekit.666666.xyz:12337

十、matrix-livekit-jwt-service

创建目录:

mkdir -p /root/data/docker_data/matrix-livekit-jwt-service
cd /root/data/docker_data/matrix-livekit-jwt-service

创建 /root/data/docker_data/matrix-livekit-jwt-service/docker-compose.yaml

services:
  matrix-livekit-jwt-service:
    image: ghcr.io/element-hq/lk-jwt-service:latest
    container_name: matrix-livekit-jwt-service
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    network_mode: host
    environment:
      LIVEKIT_JWT_BIND: ":8170"
      LIVEKIT_URL: "wss://livekit.666666.xyz:12337"
      LIVEKIT_KEY: "livekit_api_key_example"
      LIVEKIT_SECRET: "livekit_secret_example"
      LIVEKIT_FULL_ACCESS_HOMESERVERS: "666666.xyz"

这里不需要 extra_hosts,也不需要跳过 TLS 校验。JWT 通过公网 NPM 入口访问 LiveKit 和 Synapse,配置更接近真实客户端路径,排障也更直观。

启动:

docker compose up -d
docker logs -f matrix-livekit-jwt-service

验证本机:

curl -i http://127.0.0.1:8170/healthz

验证 NPM 公网入口:

curl -i https://matrix-jwt.666666.xyz:12336/healthz

十一、coturn

coturn 不走 NPM,也不走 Worker。它直接用 DNS only 域名指向公网 IP。

创建目录:

mkdir -p /root/data/docker_data/coturn/etc/coturn
cd /root/data/docker_data/coturn

生成一个 TURN shared secret,复制输出结果,后面 turnserver.conf 和 Synapse 的 turn_shared_secret 都用同一个值:

openssl rand -hex 34

创建 /root/data/docker_data/coturn/etc/coturn/turnserver.conf

# 排障时可以临时打开 verbose,稳定后建议注释掉,避免日志太多
# verbose

use-auth-secret
static-auth-secret=替换成你的 TURN shared secret
realm=coturncoturn7829.666666.xyz

listening-ip=0.0.0.0
# 公网 IPv6 不稳定时,先保持 IPv4-only。
# listening-ip=::
external-ip=你的公网IPv4/10.20.20.8

no-tls
no-dtls
pidfile=/var/tmp/turnserver.pid

min-port=50000
max-port=50200

# 如果通话时日志出现大量 403 Forbidden IP,可以先不要启用这些黑名单。
#denied-peer-ip=10.0.0.0-10.255.255.255
#denied-peer-ip=192.168.0.0-192.168.255.255
#denied-peer-ip=172.16.0.0-172.31.255.255

创建 /root/data/docker_data/coturn/docker-compose.yaml

services:
  coturn:
    image: coturn/coturn:latest
    container_name: coturn
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    network_mode: host
    command:
      - "-c"
      - "/etc/coturn/turnserver.conf"
      - "--log-file=stdout"
    volumes:
      - /root/data/docker_data/coturn/etc/coturn/turnserver.conf:/etc/coturn/turnserver.conf
      - /root/data/docker_data/coturn/etc/:/etc/

路由器/防火墙放行:

3478/tcp
3478/udp
50000-50200/udp

coturn 域名必须 DNS only,不要开 Cloudflare 代理。

十二、ntfy 通知服务

如果 ntfy 使用 host 网络,创建 /root/data/docker_data/ntfy/data/config/server.yml

base-url: "https://sms.666666.xyz:12338"
behind-proxy: true
listen-http: ":80"
log-level: warn

/root/data/docker_data/ntfy/docker-compose.yaml

services:
  ntfy:
    image: binwiederhier/ntfy:latest
    container_name: ntfy
    command:
      - serve
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - /root/data/docker_data/ntfy/data/cache:/var/cache/ntfy
      - /root/data/docker_data/ntfy/data/config:/etc/ntfy
    network_mode: host
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

启动:

docker compose up -d
docker logs -f ntfy

验证:

curl -I http://127.0.0.1/
curl -I https://sms.666666.xyz:12338/

如果你的宿主机 80/tcp 已经被别的服务占用,就不要用 host 网络。把 ntfy 放进 matrix Docker 网络,再在 NPM 里反代 ntfy:80

十三、路由器端口

这套 Worker + NPM 方案需要转发到 Docker 主机的端口:

NPM:
  12335/tcp  Synapse
  12336/tcp  JWT
  12337/tcp  LiveKit API/WebSocket
  12338/tcp  ntfy

LiveKit:
  50201-50400/udp
  17882/tcp  可选,TCP fallback

coturn:
  3478/tcp
  3478/udp
  50000-50200/udp

这里把 coturn relay 放在 50000-50200/udp,LiveKit RTC 放在 50201-50400/udp,两段连续且不重叠。小规模使用时这个范围已经够用;如果前端是 RouterOS,也可以用一条 50000-50400/udp 规则统一放行/转发。

如果公网测试时 LiveKit 日志显示 connectionType: "udp",说明主要媒体已经走 50201-50400/udp17882/tcp 不转发也能用。

十四、启动顺序

推荐顺序:

cd /root/data/docker_data/npm
docker compose up -d

cd /root/data/docker_data/synapse
docker compose up -d

cd /root/data/docker_data/livekit-server
docker compose up -d

cd /root/data/docker_data/matrix-livekit-jwt-service
docker compose up -d

cd /root/data/docker_data/coturn
docker compose up -d

cd /root/data/docker_data/ntfy
docker compose up -d

确认容器:

docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'

应该看到:

nginx-proxy-manager
matrix-synapse
livekit-server
matrix-livekit-jwt-service
coturn
ntfy
postgres16

十五、完整验证

1. DNS

确认根域通过 Cloudflare 代理,子域名直连你的公网 IP 或 DDNS:

curl -sS -H 'accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=666666.xyz&type=AAAA'
curl -sS -H 'accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=matrix.666666.xyz&type=A'

如果访问 https://666666.xyz/.well-known/matrix/client 出现 Cloudflare Error 1034: Edge IP Restricted,优先检查根域是不是错误指向了 Cloudflare IP、1.1.1.1 或保留地址。按本文改成:

AAAA  @  100::  已代理

2. Worker

curl -i https://666666.xyz/.well-known/matrix/client
curl -i https://666666.xyz/.well-known/matrix/server

应该返回 200,且包含:

"base_url":"https://matrix.666666.xyz:12335"
"livekit_service_url":"https://matrix-jwt.666666.xyz:12336"
"m.server":"matrix.666666.xyz:12335"

3. Synapse via NPM

curl -i https://matrix.666666.xyz:12335/_matrix/client/versions
curl -i https://matrix.666666.xyz:12335/_matrix/federation/v1/version

两个接口都应能从公网返回 200。第二个 federation endpoint 很重要,JWT 服务验证 OpenID 时会用到。

4. JWT via NPM

curl -i https://matrix-jwt.666666.xyz:12336/healthz

应返回 200

5. LiveKit via NPM

curl -I https://livekit.666666.xyz:12337/

LiveKit 根路径返回 200404 或简短响应都不关键,关键是 TLS 能通,NPM 能连到 10.20.20.8:7880,WebSocket 能被转发。

6. ntfy

curl -I https://sms.666666.xyz:12338/

应返回 ntfy 页面或服务响应。

7. 发起 LiveKit 呼叫

让一个客户端发起呼叫,同时看 JWT:

docker logs --since=5m matrix-livekit-jwt-service

成功时常见关键日志:

Got Matrix user info for @user:666666.xyz
Created LiveKit room sid
Using LiveKit room sid

再看 LiveKit:

docker logs --since=5m livekit-server

成功时常见关键日志:

starting RTC session
participant active
mediaTrack published
connectionType: "udp"
connectTime: 55ms

connectionType: "udp" 是最漂亮的结果,说明 LiveKit 媒体直连 UDP 成功。实际 connectTime 会因网络而异,几十毫秒到几百毫秒都正常。

如果 LiveKit 日志在挂断后出现一次 dtls timeout,但前面已经有 participant activemediaTrack published,而且通话能正常建立,这通常只是离开房间后的清理日志,不必当成配置失败。

如果 JWT 日志里偶尔出现一次 Unable to create room on SFU,随后重试成功并出现 Created LiveKit room sid,通常是瞬时连接或冷启动抖动。只有它持续出现时,才重点检查 LIVEKIT_URL、LiveKit key/secret、NPM 的 LiveKit Proxy Host 和 12337/tcp

coturn 在 LiveKit 呼叫时没有日志也正常。MatrixRTC + LiveKit 成功走 UDP 时,媒体不一定经过 coturn;coturn 主要服务旧客户端、特殊 NAT 和 fallback 场景。

十六、常见问题

1. Worker 预览根路径显示 Not Found

正常。本文 Worker 只处理:

/.well-known/matrix/client
/.well-known/matrix/server

请在 Worker 预览里访问完整路径。

2. 访问根域 well-known 出现 Error 1034

这是 Cloudflare 认为你的根域橙云记录指向了受限 Edge IP 或保留地址。检查 DNS,把根域改成:

AAAA  @  100::  已代理

然后确认 Worker 路由是:

666666.xyz/.well-known/matrix/*

3. NPM UI 看不到我手写的配置

手写 /data/nginx/proxy_host/*.conf 不会变成 NPM 数据库里的 Proxy Host,所以 UI 看不到。教程里建议全部用 NPM UI 添加 Proxy Host。这样以后改证书、开关 WebSocket、看状态都在一个地方。

4. Worker 返回正常,但客户端连不上 homeserver

检查 m.homeserver.base_url 是否是客户端能访问的地址:

https://matrix.666666.xyz:12335

再检查:

curl -i https://matrix.666666.xyz:12335/_matrix/client/versions

如果这个公网请求不通,问题在 DNS only、路由器端口转发、NPM Proxy Host 或证书,不在 Worker。

5. JWT 报 OpenID 错误

先检查 Worker 的 server delegation:

curl -i https://666666.xyz/.well-known/matrix/server

应返回:

{"m.server":"matrix.666666.xyz:12335"}

然后检查 federation endpoint:

curl -i https://matrix.666666.xyz:12335/_matrix/federation/v1/version
curl -i 'https://matrix.666666.xyz:12335/_matrix/federation/v1/openid/userinfo?access_token=test'

第一个应返回 200,第二个用假 token 返回 401 是正常的。如果第一个不通,检查 Synapse listener 是否包含 federation,以及 NPM 是否正确反代到 matrix-synapse:8008

6. LiveKit 有房间但没有媒体

NPM 只处理 7880 的 HTTPS/WSS。媒体不走 NPM:

50201-50400/udp

必须真实放行/转发到 Docker 主机。然后检查 LiveKit 日志里是否有:

connectionType: "udp"

如果没有,优先检查公网 UDP 转发、防火墙、use_external_ip: true 和 LiveKit 的 port_range_start / port_range_end

7. 该不该继续保留 coturn

建议保留。LiveKit 是新版 MatrixRTC 的媒体后端,coturn 仍然服务于旧客户端、特殊 NAT 和 fallback。二者不是替代关系。

8. 和 Tunnel 版怎么选

如果你完全不能开公网自定义端口,或者希望所有 HTTP/WSS 都走 Cloudflare 内网穿透,选 Tunnel 免 NPM 版。

如果你已经有 NPM 和端口转发,尤其是在大陆环境里 Tunnel 版呼叫慢、白屏时间长,而 coturn 本身没问题,优先选本文 Worker + NPM 版。