这篇是 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 用
12335、12336、12337、12338暴露实际服务。 - 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 Restricted。192.0.2.1 属于文档保留地址,也容易被 Cloudflare 拦住。实践里用 AAAA 100:: 做 Worker 路由占位最稳。
matrix.666666.xyz、matrix-jwt.666666.xyz、livekit.666666.xyz、sms.666666.xyz 这些自定义端口入口不要开橙云。Cloudflare 代理只支持指定的一批 HTTP/HTTPS 端口,12335、12336、12337、12338 不适合走橙云代理。
五、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-managerNPM 后台:
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: ON1. 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:123352. 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:123363. 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:123374. 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 没有加入
matrixDocker 网络。 - host 网络服务却在 NPM 里写了容器名。
- ntfy 实际监听在宿主机
80,但 NPM 写成了ntfy:80。
七、Cloudflare Worker
创建一个 Worker,例如命名为:
matrix-jwtWorker 代码:
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.xyzpublic_baseurl 才是客户端访问 Synapse 的真实 HTTPS 入口。
8008 listener 要同时包含 client 和 federation:
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
resources:
- names: [client, federation]
compress: falseMatrixRTC 配置:
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-serverNPM 里的 LiveKit Proxy Host 上游指向:
http://10.20.20.8:7880JWT 和客户端看到的 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/udpcoturn 域名必须 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/udp,17882/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 根路径返回 200、404 或简短响应都不关键,关键是 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: 55msconnectionType: "udp" 是最漂亮的结果,说明 LiveKit 媒体直连 UDP 成功。实际 connectTime 会因网络而异,几十毫秒到几百毫秒都正常。
如果 LiveKit 日志在挂断后出现一次 dtls timeout,但前面已经有 participant active、mediaTrack 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 版。