【实用的开源工具】Jitsi Meet:自己搭一个视频会议服务器
「实用的开源工具」系列:我只收集值得用的开源项目,写清楚它能做什么、怎么部署、有哪些坑,并在文末标明出处。内容整理自官方文档,核对日期 2026-10-31,具体版本以官方为准。这篇比前面几篇更依赖网络环境,建议先在一台干净的测试服务器上走一遍。
项目信息卡
| 项目 | 说明 |
|---|---|
| 名称 | Jitsi Meet |
| 做什么 | 开源的视频会议系统,浏览器和手机应用都能用;官方 README 列出高清音视频、共享屏幕、举手与表情、聊天(含私聊)、投票、虚拟背景、Web 与原生 SDK 等 |
| 许可证 | Apache-2.0(GitHub 显示:jitsi/jitsi-meet 和 Docker 部署仓库 jitsi/docker-jitsi-meet 都是) |
| 最近版本 | jitsi-meet 发布标签 stable/jitsi-meet_11248(2026-09-14);docker-jitsi-meet 发布标签 stable-11248-2(2026-10-23),均查询于 2026-10-31 |
| 项目状态 | 两个仓库都未归档,jitsi-meet 最近一次推送是 2026-10-30,docker-jitsi-meet 是 2026-10-23;手册页面标注的更新时间是 2026-10-13 |
| 部署方式 | 官方提供 Debian 软件包和 Docker 两种;这篇讲 Docker。镜像发布在 GitHub Container Registry(ghcr.io/jitsi) |
| 组成 | Web 界面、XMPP 服务器(Prosody)、会议焦点组件(Jicofo)、视频路由(Jitsi Videobridge,JVB);可选 SIP 网关(Jigasi)和录制/直播(Jibri) |
| 必须对外开放 | TCP 443、UDP 10000;TCP 80 用于跳转(详见下文) |
| 资源 | 官方要求页:网络链路最重要;内存一般建议 8 GB,小会议 4 GB,测试或很小的会议可试 2 GB;基础服务器 4 个独占核心;磁盘约 20 GB 够用 |
| 官方的提醒 | 手册要求页写明:Jitsi Meet 是实时系统,估算错误可能直接让基本功能不可用;不要在同一套环境里叠加其他功能 |
它能做什么
- 直接用浏览器开会:README 说“基于浏览器,使用很直接”,也有 Android、iOS 应用。
- 可以控制谁能创建会议:Docker 指南里有登录(
ENABLE_AUTH)、访客(ENABLE_GUESTS)、等候厅(ENABLE_LOBBY)等开关,下面会讲。 - 可选扩展:SIP 电话接入(Jigasi)、文档协作(Etherpad)、白板(Excalidraw)、录制和直播(Jibri)、转录。这些都需要额外的 Compose 文件和配置,这篇不展开。
适合:团队、社群自己搭一套内部会议。不适合:没有公网 IP、网络条件也说不清的环境,或者想“一次装好、永远不管”的人。
先弄清楚:哪些端口必须开放
官方 Docker 指南列出的、必须在防火墙放行的外部端口:
| 端口 | 协议 | 用途 |
|---|---|---|
| 80 | TCP | Web 界面 HTTP,主要用来跳转到 HTTPS(需要在 .env 里取消 ENABLE_HTTP_REDIRECT=1 的注释) |
| 443 | TCP | Web 界面 HTTPS |
| 10000 | UDP | 音视频媒体(RTP)流量 |
| 20000-20050 | UDP | 仅当你部署 Jigasi(SIP 接入)时才需要 |
两点要说清楚:
- UDP 10000 是音视频真正走的路。 网页(80/443)只负责界面和信令。它是 UDP 流量,不能像网页那样套 Nginx 反向代理。
- 这篇下面用反向代理时,只有网页部分被代理,UDP 10000 仍然要直接开放到这台服务器。
官方的防火墙示例(CentOS/Fedora 的 firewalld):
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --permanent --add-port=10000/udp
sudo firewall-cmd --reloadDebian/Ubuntu 软件包版的指南里还提到过 TCP 5349(coturn,UDP 被封锁时的回退);Docker 指南的端口清单里没有它,Docker 版是通过 TURN_* 变量去配置外部 TURN 服务器,这篇不展开。
部署(Docker,官方快速开始)
官方强调:下载最新发布包,不要 git clone 仓库(master 分支是为每日的 unstable 镜像设计的,不要配合发布镜像使用)。
第 1 步:下载并解压
官方给的命令(用 GitHub API 找到最新发布包):
mkdir -p /opt/jitsi && cd /opt/jitsi
wget $(wget -q -O - https://api.github.com/repos/jitsi/docker-jitsi-meet/releases/latest | grep zip | cut -d\" -f4)
unzip <下载到的文件名>
cd <解压出来的目录名>没有 unzip 的话先装一个。GitHub API 对未登录请求有频率限制,如果这条命令失败,可以到仓库的 Releases 页面手动下载 zip(这是我的补充)。我查询到的最新发布标签是 stable-11248-2,发布包里的 Compose 文件默认就用这个版本的镜像(${JITSI_IMAGE_VERSION:-stable-11248-2}),所以不需要再手动固定标签。
第 2 步:创建 .env 并生成密码
cp env.example .env
./gen-passwords.sh官方说明:gen-passwords.sh 用 openssl rand -hex 16 为必需的内部账号生成强密码,会改写 .env 并保留备份 .env.bak;这个项目早先有默认密码,现在去掉了,没设置密码容器就不会启动;每个密码不要复用。
第 3 步:创建配置目录
官方命令(适用于 stable-11146 及更新版本,容器以非 root 用户运行且文件系统只读):
mkdir -p ~/.jitsi-meet-cfg/{web,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri,transcriber}
mkdir -p ~/.jitsi-meet-cfg/storage/{jibri,prosody,transcripts,web}
mkdir -p ~/.jitsi-meet-cfg/tmp/{web-crontabs,web-load-test}
chmod 777 ~/.jitsi-meet-cfg/storage/{jibri,prosody,transcripts,web}
chmod 777 ~/.jitsi-meet-cfg/tmp/{web-crontabs,web-load-test}官方警告:容器以 uid/gid 1000 的非特权用户运行,storage 和 tmp 目录必须由这个用户可写;要在第一次启动前自己建好并执行 chmod,让它们被自动创建会得到容器写不进去的目录;目录缺失或不可写时,容器会带着明确的错误拒绝启动。这个路径 ~/.jitsi-meet-cfg 就是 env.example 里 CONFIG 的默认值。
第 4 步:改 .env(按你的 HTTPS 方案选一种)
无论哪种,.env 里的 PUBLIC_URL 都是必填的(env.example 注释写的是 required;手册也强调真实使用时必须设成真实域名)。
方案 A:Jitsi 自己处理 HTTPS(内置 Let's Encrypt)。 适合这台机器直接对公网、没有别的 Web 服务占用 80/443。手册的必需项:
HTTP_PORT=80
HTTPS_PORT=443
PUBLIC_URL=https://meet.example.com
ENABLE_LETSENCRYPT=1
LETSENCRYPT_DOMAIN=meet.example.com
[email protected]
ENABLE_HTTP_REDIRECT=1ENABLE_HTTP_REDIRECT=1 是手册建议的“把 HTTP 跳转到 HTTPS”。LETSENCRYPT_EMAIL 手册标为必填。第一次测试时,.env 里还有 LETSENCRYPT_USE_STAGING=1,用来避开频率限制,见踩坑清单。
方案 B:前面已有反向代理,由代理处理 HTTPS。 手册的做法是在 .env 里关掉 Jitsi 自己的 HTTPS:
HTTP_PORT=8000
PUBLIC_URL=https://meet.example.com
DISABLE_HTTPS=1
ENABLE_HTTP_REDIRECT=0
ENABLE_LETSENCRYPT=0再编辑发布包里的 docker-compose.yml,把 web 服务的端口改成只监听本机(手册里的写法):
ports:
- '127.0.0.1:${HTTP_PORT}:8000'
- '127.0.0.1:${HTTPS_PORT}:8443'原来是 '${HTTP_PORT}:8000' 和 '${HTTPS_PORT}:8443',会绑定在所有网卡上。官方 Compose 里 jvb 的 UDP 端口是 '${JVB_PORT:-10000}:${JVB_PORT:-10000}/udp',要保持对外开放,不要改成 127.0.0.1。
第 5 步:启动
docker compose up -d
docker compose logs -f web官方说明:直接访问是 https://localhost:8443(或你在 .env 里改过的端口);默认的自签名证书,移动端应用不能用自签名证书。HTTP(默认 8000 端口)只适合给反向代理用,直接用 HTTP 访问会出现 getUserMedia 之类的麦克风、摄像头错误。
反向代理(Nginx)
只在方案 B 时需要。官方说明:默认通过 WebSocket 传输信令(XMPP),所以反向代理必须正确转发 WebSocket,路径是 /xmpp-websocket,其他 HTTP 请求都交给 web 容器。官方的 Nginx 片段(8000 对应 HTTP_PORT),我只在外面套了 server 块并换成 example.com:
server {
listen 443 ssl http2;
server_name meet.example.com;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
location /xmpp-websocket {
proxy_pass http://localhost:8000$request_uri;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_pass http://localhost:8000/;
proxy_http_version 1.1;
}
}官方还给了 Apache 的写法(需要 mod_proxy、mod_proxy_http、mod_proxy_wstunnel)。另外官方明确说:Jitsi Meet 目前部署在子目录下表现不好,请用独立域名。
登录、访客和等候厅
官方的认证说明:
ENABLE_AUTH=1开启认证;AUTH_TYPE可选internal、jwt、ldap(env.example还列了matrix)。ENABLE_GUESTS=1允许访客:开启后,没登录的人要等已登录的用户进入房间后才能加入;不开启访客,所有人都必须先登录才能加入。- 开启认证后,已登录用户在会话超时前会自动保持登录。可以设
ENABLE_AUTO_LOGIN=0关掉自动登录,或用JICOFO_AUTH_LIFETIME限制会话时长。
最简单的 internal 模式(用 XMPP 凭据)需要自己建用户。.env 里设:
ENABLE_AUTH=1
ENABLE_GUESTS=1
AUTH_TYPE=internal改完重启(docker compose up -d),然后在 prosody 容器里用官方命令建用户:
docker compose exec prosody /bin/bash
# 进入容器后:
prosodyctl --config /run/prosody/config/prosody.cfg.lua register 用户名 meet.jitsi 密码官方说明这条命令没有任何输出;删除用户用同样格式的 unregister 命令;列出用户用:
find /var/lib/prosody/data/meet%2ejitsi/accounts -type f -exec basename {} .dat \;等候厅由 ENABLE_LOBBY 控制(手册的示例值是 1),预加入页面是 ENABLE_PREJOIN_PAGE,欢迎页是 ENABLE_WELCOME_PAGE。想改 config.js 或 interface_config.js 里的设置,官方说它们每次启动都在容器里重新生成、不会写回主机,应该在 CONFIG/web/ 目录里建 custom-config.js 和 custom-interface_config.js,只写你要改的部分,脚本会把它们追加到生成的文件后面。
第一次使用
- 方案 A 打开
https://meet.example.com,方案 B 同样打开你的域名;只在本机试的话,打开https://localhost:8443(浏览器会因为自签名证书警告)。 - 没开认证时,直接按页面进入会议。开了认证,需要先用上面
prosodyctl register建好的账号登录;只有访客时要等已登录的人先进房间。 - 找一台不在同一局域网的手机或电脑,用蜂窝网络加入,看双方能不能看到和听到彼此。这一步专门检验 UDP 10000 和
JVB_ADVERTISE_IPS:官方提醒,设置不正确时,通话人数超过两个就会出问题。 - 官方文档里没有写“如何创建第一个会议室”之类的界面操作,这里不编造,请以你的界面为准。
NAT、局域网和资源
NAT 或局域网环境。 官方原话意思:在局域网中,或者在公网上通过 NAT,都应该设置 JVB_ADVERTISE_IPS,它决定 JVB 向客户端宣告哪些 IP 和端口用于音视频媒体。即使用了反向代理也必须设,因为它是接收媒体流量的 IP,反向代理不管这部分。如果用户从互联网进来,通常是你的公网 IP。没设对,超过两个人的会议会崩。
JVB_ADVERTISE_IPS=203.0.113.10- 官方说明:公网 IP 会尝试通过 STUN 自动发现,STUN 服务器由
JVB_STUN_SERVERS指定,手册列出的默认值是stun.l.google.com:19302等三个。 - 内外网客户端用不同 IP(split horizon)时,可以用逗号写多个,例如
192.168.1.1,1.2.3.4;外部端口和内部JVB_PORT不同时,用#分隔:192.168.1.1#12345。 - 离线/内网隔离环境没有互联网,官方要求设
JVB_DISABLE_STUN=true。 - 路由器后面的服务器:官方 Debian 指南要求在路由器上把端口转发到服务器(Docker 指南的端口是 80、443、
10000/udp)。
UDP 缓冲区。 官方说明:如果出现音画不同步、掉帧,或预期有大流量、大会议,可能需要在运行 jvb 容器的宿主机上调整 UDP 缓冲区,手册提供了一个 sysctl 配置文件,保存到 /etc/sysctl.d 后用 sysctl --system 加载。
资源。 前面信息卡里已写。几个具体数字来自官方要求页:分辨率与带宽的大致对应是 180p 约 200 kbit/s,360p 约 500 kbit/s,720p 约 2.5 Mbit/s,4K 约 10 Mbit/s;朋友或小团队的服务器 1 Gbit/s 带宽通常够用;Prosody 只能使用一个核心,所以核心数过多不一定有用。env.example 里有两个被注释掉的内存限制变量:JICOFO_MAX_MEMORY=3072m 和 VIDEOBRIDGE_MAX_MEMORY=3072m。录制(Jibri)需要一台独立系统对应一场会议,官方不建议和 Jitsi Meet 放在同一台服务器。
更新与卸载
更新(官方步骤):再执行一遍下载最新发布包的命令,解压并在询问时选择覆盖所有文件。
cd /opt/jitsi/<解压出来的目录名>
cp .env .env.bak-$(date +%F)
cp -a ~/.jitsi-meet-cfg ~/.jitsi-meet-cfg.bak-$(date +%F) # 通用做法:先备份配置目录
# 在上一级目录重新下载并解压覆盖,方法同上
docker compose pull
docker compose up -d.env不在发布包里(包里是env.example),所以通常不会被覆盖,更新后确认它还在;新版本可能有新的变量,对照新的env.example看一眼。这是我的判断,不是官方原话。- 方案 B 里你改过的
docker-compose.yml会被覆盖,更新后要重新把端口改回127.0.0.1。 - 如果从早于
stable-11146的版本升级,官方说明要先建新增的storage、tmp目录并设为可写,而且部分路径变了:Prosody 数据和 Web 的 TLS 材料首次启动会自动迁移,Jibri 录制/日志和转录文件需要自己移动,旧目录不会被删除,确认新的能用后才可以删。
卸载:
cd /opt/jitsi/<解压出来的目录名>
docker compose down之后才是删除数据:~/.jitsi-meet-cfg。注意官方关于 Let's Encrypt 的警告:证书有签发频率限制,如果计划删掉 .jitsi-meet-cfg 文件夹,建议先把它挪到别处保存,而不是直接删,里面有已经签发的证书。
踩坑清单
- UDP 10000 必须直达服务器。 它是音视频流量,防火墙、云安全组和路由器端口转发都要放行;反向代理管不了它。网页能打开、但互相看不到画面,先查这个。
- NAT 和反向代理场景都要设
JVB_ADVERTISE_IPS。 官方说设置不对会在超过两人入会时出问题,并且“用了反向代理也一样”。 PUBLIC_URL必须是真实域名。 手册把它标为真实部署的必改项;用了非标准 HTTPS 端口时,URL 里也要带端口。- 别用 HTTP 直接访问。 官方说明 HTTP 访问会触发 WebRTC 的
getUserMedia相关错误,只用于反向代理后面。 - 移动端不接受自签名证书。 默认证书是自签名的,要给手机用就必须换成真正的证书,用内置 Let's Encrypt,或者挂载自己的证书。
- Let's Encrypt 有频率限制。 官方提到:同一个域名每 7 天最多 5 张重复证书(以官方撰文时的限制为准)。测试期间可用
LETSENCRYPT_USE_STAGING=1;从 staging 切到正式时,要手动清空.jitsi-meet-cfg/storage/web里的证书。env.example还写了 ACME 服务器默认是 zerossl,想用 Let's Encrypt 要设LETSENCRYPT_ACME_SERVER="letsencrypt"(以你拿到的env.example为准)。 - 用自己的证书要挂载到固定路径。 官方说明:把证书链文件挂到
web容器的/config/keys/cert.crt,私钥挂到/config/keys/cert.key,启动时它们会被复制进storage卷,取代自签证书。 - 配置目录要在第一次启动前建好,并设置可写。 容器是 uid/gid 1000 的非特权用户,只读文件系统;缺目录或不可写,容器会拒绝启动。官方命令里的
chmod 777权限很宽松,这是官方的做法;想收紧的话,把storage和tmp下这些目录的属主改为 uid 1000 也能满足“可写”要求(这是我的通用思路,不是官方说法)。 - 没生成密码,容器不会启动。 一定要运行
./gen-passwords.sh,并且不要复用密码。 - 更新会覆盖你改过的
docker-compose.yml。 方案 B 的127.0.0.1端口绑定、以及你对 Compose 文件做的其他修改,更新后要重做。 - Docker 发布的端口通常会绕过 ufw 之类的防火墙(通用经验)。官方 Compose 里
web的端口默认绑定所有网卡,方案 B 就是为了改成只监听本机。 - 不要把 Jitsi 部署在子目录。 官方明确说表现不好。
- 不要在同一台机器叠加别的负载。 官方要求页的原意:实时系统,估算错误会直接破坏基本功能;Jibri 录制尤其吃资源,也可能耗尽磁盘,让 Jitsi Meet 停摆。
- JVB 默认用外部 STUN 服务器发现公网 IP。 文档列出的默认值是 Google 的 STUN 服务器;介意的话,手动设置
JVB_ADVERTISE_IPS,离线环境则设JVB_DISABLE_STUN=true。 env.example对 JaaS 的备注我没有核实。 其中关于ENABLE_JAAS_COMPONENTS的注释写着“如果启用了 Let's Encrypt,将用LETSENCRYPT_EMAIL自动创建 JaaS 账号”。该选项标注为 beta、默认关闭;我没有读到它的完整行为说明,如有疑虑请只保留ENABLE_JAAS_COMPONENTS=0并读官方原文。
出处
- Jitsi Meet 官方仓库(功能、许可证、部署入口)
- 官方手册:Self-Hosting Guide - Docker
- 官方手册:Self-Hosting Guide - Debian/Ubuntu server(对比 TCP 5349、路由器转发)
- 官方手册:Requirements(资源与带宽)
- docker-jitsi-meet 仓库(镜像标签、README)
- docker-jitsi-meet:env.example(发布 stable-11248-2)
- docker-jitsi-meet:docker-compose.yml(发布 stable-11248-2)
- docker-jitsi-meet:gen-passwords.sh
有想法或问题?去留言板留言 →