【实用的开源工具】Jitsi Meet:自己搭一个视频会议服务器

阅读约 14 分钟
【实用的开源工具】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 指南列出的、必须在防火墙放行的外部端口:

端口协议用途
80TCPWeb 界面 HTTP,主要用来跳转到 HTTPS(需要在 .env 里取消 ENABLE_HTTP_REDIRECT=1 的注释)
443TCPWeb 界面 HTTPS
10000UDP音视频媒体(RTP)流量
20000-20050UDP仅当你部署 Jigasi(SIP 接入)时才需要

两点要说清楚:

  1. UDP 10000 是音视频真正走的路。 网页(80/443)只负责界面和信令。它是 UDP 流量,不能像网页那样套 Nginx 反向代理。
  2. 这篇下面用反向代理时,只有网页部分被代理,UDP 10000 仍然要直接开放到这台服务器。

官方的防火墙示例(CentOS/Fedora 的 firewalld):

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

Debian/Ubuntu 软件包版的指南里还提到过 TCP 5349(coturn,UDP 被封锁时的回退);Docker 指南的端口清单里没有它,Docker 版是通过 TURN_* 变量去配置外部 TURN 服务器,这篇不展开。

部署(Docker,官方快速开始)

官方强调:下载最新发布包,不要 git clone 仓库(master 分支是为每日的 unstable 镜像设计的,不要配合发布镜像使用)。

第 1 步:下载并解压

官方给的命令(用 GitHub API 找到最新发布包):

bash
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 并生成密码

bash
cp env.example .env
./gen-passwords.sh

官方说明:gen-passwords.sh 用 openssl rand -hex 16 为必需的内部账号生成强密码,会改写 .env 并保留备份 .env.bak;这个项目早先有默认密码,现在去掉了,没设置密码容器就不会启动;每个密码不要复用。

第 3 步:创建配置目录

官方命令(适用于 stable-11146 及更新版本,容器以非 root 用户运行且文件系统只读):

bash
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。手册的必需项:

ini
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=1

ENABLE_HTTP_REDIRECT=1 是手册建议的“把 HTTP 跳转到 HTTPS”。LETSENCRYPT_EMAIL 手册标为必填。第一次测试时,.env 里还有 LETSENCRYPT_USE_STAGING=1,用来避开频率限制,见踩坑清单。

方案 B:前面已有反向代理,由代理处理 HTTPS。 手册的做法是在 .env 里关掉 Jitsi 自己的 HTTPS:

ini
HTTP_PORT=8000
PUBLIC_URL=https://meet.example.com
DISABLE_HTTPS=1
ENABLE_HTTP_REDIRECT=0
ENABLE_LETSENCRYPT=0

再编辑发布包里的 docker-compose.yml,把 web 服务的端口改成只监听本机(手册里的写法):

yaml
        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 步:启动

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

nginx
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 里设:

ini
ENABLE_AUTH=1
ENABLE_GUESTS=1
AUTH_TYPE=internal

改完重启(docker compose up -d),然后在 prosody 容器里用官方命令建用户:

bash
docker compose exec prosody /bin/bash
# 进入容器后:
prosodyctl --config /run/prosody/config/prosody.cfg.lua register 用户名 meet.jitsi 密码

官方说明这条命令没有任何输出;删除用户用同样格式的 unregister 命令;列出用户用:

bash
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,只写你要改的部分,脚本会把它们追加到生成的文件后面。

第一次使用

  1. 方案 A 打开 https://meet.example.com,方案 B 同样打开你的域名;只在本机试的话,打开 https://localhost:8443(浏览器会因为自签名证书警告)。
  2. 没开认证时,直接按页面进入会议。开了认证,需要先用上面 prosodyctl register 建好的账号登录;只有访客时要等已登录的人先进房间。
  3. 找一台不在同一局域网的手机或电脑,用蜂窝网络加入,看双方能不能看到和听到彼此。这一步专门检验 UDP 10000 和 JVB_ADVERTISE_IPS:官方提醒,设置不正确时,通话人数超过两个就会出问题。
  4. 官方文档里没有写“如何创建第一个会议室”之类的界面操作,这里不编造,请以你的界面为准。

NAT、局域网和资源

NAT 或局域网环境。 官方原话意思:在局域网中,或者在公网上通过 NAT,都应该设置 JVB_ADVERTISE_IPS,它决定 JVB 向客户端宣告哪些 IP 和端口用于音视频媒体。即使用了反向代理也必须设,因为它是接收媒体流量的 IP,反向代理不管这部分。如果用户从互联网进来,通常是你的公网 IP。没设对,超过两个人的会议会崩。

ini
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 放在同一台服务器。

更新与卸载

更新(官方步骤):再执行一遍下载最新发布包的命令,解压并在询问时选择覆盖所有文件。

bash
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 录制/日志和转录文件需要自己移动,旧目录不会被删除,确认新的能用后才可以删。

卸载:

bash
cd /opt/jitsi/<解压出来的目录名>
docker compose down

之后才是删除数据:~/.jitsi-meet-cfg。注意官方关于 Let's Encrypt 的警告:证书有签发频率限制,如果计划删掉 .jitsi-meet-cfg 文件夹,建议先把它挪到别处保存,而不是直接删,里面有已经签发的证书。

踩坑清单

  1. UDP 10000 必须直达服务器。 它是音视频流量,防火墙、云安全组和路由器端口转发都要放行;反向代理管不了它。网页能打开、但互相看不到画面,先查这个。
  2. NAT 和反向代理场景都要设 JVB_ADVERTISE_IPS。 官方说设置不对会在超过两人入会时出问题,并且“用了反向代理也一样”。
  3. PUBLIC_URL 必须是真实域名。 手册把它标为真实部署的必改项;用了非标准 HTTPS 端口时,URL 里也要带端口。
  4. 别用 HTTP 直接访问。 官方说明 HTTP 访问会触发 WebRTC 的 getUserMedia 相关错误,只用于反向代理后面。
  5. 移动端不接受自签名证书。 默认证书是自签名的,要给手机用就必须换成真正的证书,用内置 Let's Encrypt,或者挂载自己的证书。
  6. 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 为准)。
  7. 用自己的证书要挂载到固定路径。 官方说明:把证书链文件挂到 web 容器的 /config/keys/cert.crt,私钥挂到 /config/keys/cert.key,启动时它们会被复制进 storage 卷,取代自签证书。
  8. 配置目录要在第一次启动前建好,并设置可写。 容器是 uid/gid 1000 的非特权用户,只读文件系统;缺目录或不可写,容器会拒绝启动。官方命令里的 chmod 777 权限很宽松,这是官方的做法;想收紧的话,把 storage 和 tmp 下这些目录的属主改为 uid 1000 也能满足“可写”要求(这是我的通用思路,不是官方说法)。
  9. 没生成密码,容器不会启动。 一定要运行 ./gen-passwords.sh,并且不要复用密码。
  10. 更新会覆盖你改过的 docker-compose.yml。 方案 B 的 127.0.0.1 端口绑定、以及你对 Compose 文件做的其他修改,更新后要重做。
  11. Docker 发布的端口通常会绕过 ufw 之类的防火墙(通用经验)。官方 Compose 里 web 的端口默认绑定所有网卡,方案 B 就是为了改成只监听本机。
  12. 不要把 Jitsi 部署在子目录。 官方明确说表现不好。
  13. 不要在同一台机器叠加别的负载。 官方要求页的原意:实时系统,估算错误会直接破坏基本功能;Jibri 录制尤其吃资源,也可能耗尽磁盘,让 Jitsi Meet 停摆。
  14. JVB 默认用外部 STUN 服务器发现公网 IP。 文档列出的默认值是 Google 的 STUN 服务器;介意的话,手动设置 JVB_ADVERTISE_IPS,离线环境则设 JVB_DISABLE_STUN=true。
  15. env.example 对 JaaS 的备注我没有核实。 其中关于 ENABLE_JAAS_COMPONENTS 的注释写着“如果启用了 Let's Encrypt,将用 LETSENCRYPT_EMAIL 自动创建 JaaS 账号”。该选项标注为 beta、默认关闭;我没有读到它的完整行为说明,如有疑虑请只保留 ENABLE_JAAS_COMPONENTS=0 并读官方原文。

出处

想要类似的东西?网站、Telegram 机器人、运维工具,说说你的需求。
发布需求

有想法或问题?去留言板留言 →