Matrix Synapse 接入 LiveKit:公网 443 标准部署方案

整理有公网 80/443 端口时最省事的 MatrixRTC / LiveKit / JWT / coturn 部署方案:Nginx Proxy Manager 使用标准 HTTPS,Synapse、JWT、LiveKit 分域名反代,LiveKit 和 coturn 媒体端口直通。

这篇记录有公网 80/443 时最干净的一套 Matrix Synapse + LiveKit 部署方案。和前面两篇不同,这里不需要 Cloudflare Tunnel,也不需要 Cloudflare Worker:公网 443 直接交给 Nginx Proxy Manager(NPM),按域名把请求反代到 Synapse、matrix-livekit-jwt-service 和 LiveKit。

三套方案的选择可以这样理解:

  • 没有公网 443,又想内网穿透:用 Cloudflare Tunnel 免 NPM 版。
  • 没有本地 443,但能开放非标准端口且想继续用 NPM:用 Cloudflare Worker + NPM 版。
  • 有公网 80/443:用本文的公网 443 标准版,最省心。

本文示例:

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

NPM:
  80/tcp
  443/tcp

HTTPS 域名:
  https://666666.xyz              -> Synapse 8008
  https://matrix-jwt.666666.xyz   -> JWT service 8170
  https://livekit.666666.xyz      -> LiveKit 7880

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

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

实际部署时,把 666666.xyz、IP、所有密钥都换成自己的。不要把 LiveKit secret、TURN shared secret、Synapse secret 写进公开文章或截图里。

一、整体结构

公网 443 方案里,请求路径最直接:

Element 客户端
  |
  | HTTPS 443 / WSS 443
  v
Nginx Proxy Manager
  |-- 666666.xyz              -> matrix-synapse:8008
  |-- matrix-jwt.666666.xyz   -> 10.20.20.8:8170
  |-- livekit.666666.xyz      -> 10.20.20.8:7880

LiveKit 媒体:
  50201-50400/udp 直接到 Docker 主机

coturn:
  3478/tcp、3478/udp、50000-50200/udp 直接到 Docker 主机

这里 NPM 只处理 HTTP、HTTPS 和 WebSocket。LiveKit 的 UDP 媒体端口、coturn 的 TURN 端口都不经过 NPM。

二、DNS 规划

推荐这样建 DNS 记录:

记录 指向 说明 Cloudflare 状态
666666.xyz 公网 IP Matrix homeserver,Synapse 和 well-known 都在这里 可橙云或 DNS only
matrix-jwt.666666.xyz 公网 IP JWT service 可橙云或 DNS only
livekit.666666.xyz 公网 IP LiveKit API/WebSocket 可橙云或 DNS only
sms.666666.xyz 公网 IP ntfy 通知服务 可橙云或 DNS only
coturncoturn7829.666666.xyz 公网 IP coturn TURN 必须 DNS only

这里的 sms.666666.xyz 就是 ntfy 的访问域名。公网 443 方案里,ntfy 不需要单独暴露宿主机端口;后面在 NPM 里新增一个 Proxy Host,把 sms.666666.xyz 反代到 ntfy 容器即可。

如果 DNS 托管在 Cloudflare:

  • 666666.xyzmatrix-jwt.666666.xyzlivekit.666666.xyzsms.666666.xyz 可以橙云或 DNS only;如果遇到 WebSocket、证书或端口策略问题,先改 DNS only 排障。
  • coturncoturn7829.666666.xyz 必须 DNS only。TURN 不能走 Cloudflare HTTP 代理。

公网服务器安全组、路由器或防火墙至少放行:

80/tcp
443/tcp

50201-50400/udp

3478/tcp
3478/udp
50000-50200/udp

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

17882/tcp 是 LiveKit TCP fallback。UDP 正常时可以不放行;如果希望 UDP 受限网络也有备用路径,再开放它。

三、NPM 标准 80/443

公网 443 版不需要 12335/12336/12337 这类非标准端口。NPM 直接监听宿主机 80443

创建目录:

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

创建 matrix 网络:

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
    ports:
      - "80:80"
      - "443:443"
      - "81:81"
    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://服务器IP:81

四、NPM Proxy Host

添加三个 Proxy Host。

1. Synapse

项目
Domain Names 666666.xyz
Scheme http
Forward Hostname / IP matrix-synapse
Forward Port 8008
Websockets Support 开启
SSL 申请 666666.xyz 证书,开启 Force SSL

因为 Synapse 接入 matrix Docker 网络,NPM 可以直接用容器名 matrix-synapse 访问它。

外部地址:

https://666666.xyz

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 证书,开启 Force SSL

JWT 服务推荐 host 网络,所以 NPM 上游写 Docker 主机内网 IP:

http://10.20.20.8:8170

外部地址:

https://matrix-jwt.666666.xyz

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 证书,开启 Force SSL

LiveKit 的 WebSocket/API 走 NPM,媒体 UDP 不走 NPM。

外部地址:

wss://livekit.666666.xyz

4. ntfy

ntfy 是普通 HTTP 服务,不需要 host 网络。这里和 Synapse 一样接入 matrix Docker 网络,让 NPM 直接通过容器名反代到 ntfy:80。这样不用额外占宿主机端口,也和本文的 NPM 部署方式一致。

项目
Domain Names sms.666666.xyz
Scheme http
Forward Hostname / IP ntfy
Forward Port 80
Websockets Support 开启
SSL 申请 sms.666666.xyz 证书,开启 Force SSL

外部地址:

https://sms.666666.xyz

五、Synapse

Synapse 继续接入 matrix 网络。

/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
    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://666666.xyz"

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"

TURN 配置:

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

六、well-known

公网 443 版最省事的做法是让 https://666666.xyz/.well-known/matrix/clientserver 都通过 NPM 转到 Synapse,然后由 Synapse 或独立 well-known 配置返回。

更稳妥、也更直观的做法是在 NPM 里给 666666.xyz 增加两个 Custom locations,直接返回静态 JSON。NPM 本身不太适合在界面里写 return,所以也可以保留一个独立 matrix-well-known-web 容器,然后在 NPM 里把 /.well-known 转发到它。

如果你希望结构最简单,可以保留前面 Tunnel 版里的 well-known 容器:

services:
  matrix-well-known-web:
    image: nginx:alpine
    container_name: matrix-well-known-web
    restart: unless-stopped
    ports:
      - "8899:80"
    volumes:
      - ./web:/usr/share/nginx/html:ro
      - ./config/default.conf:/etc/nginx/conf.d/default.conf:ro

NPM 里对 666666.xyz 增加 Custom locations:

Location Scheme Forward Hostname / IP Forward Port
/.well-known http 10.20.20.8 8899

well-known client

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

well-known server

{
  "m.server": "666666.xyz:443"
}

验证:

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

如果不想要独立 well-known 容器,也可以把 /.well-known/matrix/client 交给 Synapse 的 extra_well_known_client_content,但独立 Nginx 容器更容易看见和排障。

七、LiveKit server

LiveKit 使用 host 网络。

创建目录:

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
    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

JWT 里使用的 LiveKit 地址:

wss://livekit.666666.xyz

八、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
    network_mode: host
    environment:
      LIVEKIT_JWT_BIND: ":8170"
      LIVEKIT_URL: "wss://livekit.666666.xyz"
      LIVEKIT_KEY: "livekit_api_key_example"
      LIVEKIT_SECRET: "livekit_secret_example"
      LIVEKIT_FULL_ACCESS_HOMESERVERS: "666666.xyz"

启动:

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

验证:

curl -i http://127.0.0.1:8170/healthz
curl -i https://matrix-jwt.666666.xyz/healthz

公网 443 方案里,JWT 校验 Synapse OpenID 时会发现 666666.xyz:443,然后通过 NPM 访问 Synapse federation endpoint。通常不需要像 Tunnel 版那样再做本机 federation proxy。

九、coturn

coturn 继续 host 网络,直接监听宿主机。

先生成一个 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
    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/

启动:

docker compose up -d
docker logs -f coturn

确认 coturn DNS 是 DNS only,不要走 Cloudflare HTTP 代理。

十、ntfy 通知服务

创建目录:

mkdir -p /root/data/docker_data/ntfy/data/cache
mkdir -p /root/data/docker_data/ntfy/data/config
cd /root/data/docker_data/ntfy

创建 /root/data/docker_data/ntfy/data/config/server.yml

base-url: "https://sms.666666.xyz"
behind-proxy: true
log-level: info

创建 /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
    networks:
      - matrix
    restart: unless-stopped

networks:
  matrix:
    external: true
    name: matrix

启动并验证:

docker compose up -d
docker logs -f ntfy
docker exec nginx-proxy-manager curl -I http://ntfy/
curl -I https://sms.666666.xyz/

sms.666666.xyz 只是示例域名。实际部署时换成自己的通知域名,截图和公开文章里不要保留真实域名。

十一、启动顺序

推荐顺序:

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/matrix-well-known
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

十二、验证

1. Synapse

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

2. well-known

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

3. JWT

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

4. LiveKit

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

5. 发起呼叫

看 JWT:

docker logs -f matrix-livekit-jwt-service

应出现:

Got Matrix user info for @user:666666.xyz (full access)
Created LiveKit room sid: RM_xxx

看 LiveKit:

docker logs -f livekit-server

应出现:

starting RTC session
participant active
mediaTrack published
connectionType: udp

6. ntfy

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

十三、常见问题

还需要 Worker 或 Tunnel 吗

不需要。公网 443 已经由 NPM 接管,https://666666.xyz/.well-known/matrix/client 可以直接从你的服务器返回,不需要 Cloudflare Worker 或 Tunnel 绕路。

NPM 能不能代理 LiveKit 媒体

不能。NPM 只代理 7880 的 HTTPS/WebSocket。LiveKit 媒体仍然靠:

50201-50400/udp

如果日志里是 connectionType: udp,说明媒体链路正常。17882/tcp 只是 TCP fallback。

coturn 要不要经过 NPM

不要。coturn 直接用 DNS only 域名指向公网 IP,并直接放行:

3478/tcp
3478/udp
50000-50200/udp

Matrix 用户名能不能还是 @user:666666.xyz

可以。本文就是按 server_name: "666666.xyz" 写的。客户端登录时使用:

@user:666666.xyz

后端实际仍然可以分成:

666666.xyz
matrix-jwt.666666.xyz
livekit.666666.xyz
coturncoturn7829.666666.xyz

这是三套方案里最推荐的吗

如果你有稳定公网 80/443,是的。公网 443 标准版链路最短,排障最直观,客户端兼容性也最好。Tunnel 和 Worker 方案主要是为了绕过没有本地 443 的限制。