这篇记录有公网 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.xyz、matrix-jwt.666666.xyz、livekit.666666.xyz、sms.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 直接监听宿主机 80 和 443。
创建目录:
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.xyz2. 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.xyz3. 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.xyz4. 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: 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"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/client 和 server 都通过 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:roNPM 里对 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-serverJWT 里使用的 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/version2. well-known
curl -i https://666666.xyz/.well-known/matrix/client
curl -i https://666666.xyz/.well-known/matrix/server3. JWT
curl -i https://matrix-jwt.666666.xyz/healthz4. LiveKit
curl -I https://livekit.666666.xyz5. 发起呼叫
看 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: udp6. 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/udpMatrix 用户名能不能还是 @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 的限制。