Jitsi autoalojado vs Zoom: lo que cambia de verdad
Los servicios de videoconferencia de consumo (Zoom, Google Meet, Teams) imponen límites de duración en los planes gratuitos, cuotas de participantes, y conservan sus grabaciones en sus propios servidores. Jitsi Meet, autoalojado, elimina estas restricciones: salas sin límite, duración sin límite, ninguna cuenta requerida para unirse a una reunión, flujos de video que permanecen en su infraestructura. Es especialmente pertinente para una organización preocupada por la confidencialidad de sus reuniones, para un despacho que trata datos sensibles, o para integrar la videoconferencia en una aplicación de terceros (Jitsi ofrece una API iframe estable). El coste de funcionamiento se reduce a la factura del VPS — ninguna suscripción por puesto, ningún sobrecoste de software.
Beneficios concretos de un Jitsi autoalojado
- Reuniones sin límite de duración ni de número de salas — ningún temporizador que corte la reunión.
- Ninguna cuenta requerida para los participantes: basta un enlace para unirse.
- API iframe para integrar la videoconferencia directamente en su aplicación o en su sitio web.
- Cifrado del transporte (DTLS-SRTP) y control total sobre las eventuales grabaciones.
- Personalización completa de la interfaz: logotipo, colores, nombre de dominio de las salas.
- Ninguna telemetría enviada a terceros — los flujos de video no salen de su VPS.
Requisitos previos con cifras antes de empezar
Jitsi se dimensiona por el ancho de banda, no por la CPU. La documentación de Jitsi recomienda 8 GB de RAM (4 GB bastan para reuniones pequeñas), unos 4 núcleos dedicados para un servidor básico y, sobre todo, un enlace de red fiable: un flujo 720p consume unos 2,5 Mbit/s, y un enlace de 100 Mbit/s no sostiene a una veintena de participantes en alta definición. Vigile la carga de red del JVB. Tres puertos deben ser accesibles desde internet: TCP 443 (HTTPS), TCP 80 (desafío de Let's Encrypt y redirección a HTTPS), UDP 10000 (medios JVB). El puerto TCP 4443, citado por guías antiguas, ya no figura en la lista oficial. Si coturn se ejecuta en el mismo VPS, añada sus puertos: 3478 en UDP y TCP, 5349 en TLS (el 443 ya lo ocupa el contenedor web). Se necesita Docker con Compose V2 (comando docker compose), un nombre de dominio meet.sudominio.com con un registro A que apunte a la IP pública del VPS, y una IP pública dedicada y estable — una IP compartida complica seriamente la configuración de coturn.
Despliegue paso a paso: Docker, .env, coturn y primera prueba
Obtener docker-jitsi-meet
Descargue la última release desde la página de releases de
github.com/jitsi/docker-jitsi-meety descomprímala: la guía oficial desaconseja clonar el repositorio, cuya rama principal usa las imágenesunstable. Cree las carpetas de configuración que indica la guía (~/.jitsi-meet-cfg/…, incluidasstorageytmp, con escritura para el uid 1000): los contenedores se ejecutan sin root y, si no, se niegan a arrancar. Copieenv.examplea.env, luego ejecute./gen-passwords.sh— este script genera todos los secretos internos (Prosody, Jicofo, JVB). No se salte este paso: los valores por defecto no son secretos. La releasestable-11248-3(noviembre 2026) es la última estable; evitemastery las imágenesunstableen producción — los componentes internos cambian sin previo aviso.Configurar PUBLIC_URL, los puertos y JVB_ADVERTISE_IPS
En
.env, fije primeroHTTP_PORT=80yHTTPS_PORT=443(8000 y 8443 por defecto). Después, dos variables son críticas:PUBLIC_URL=https://meet.sudominio.com(utilizada por el cliente web para construir las URL de las salas) yJVB_ADVERTISE_IPS=<IP_PUBLICA_DEL_VPS>(la dirección que el JVB anuncia a los clientes para establecer la conexión de medios directa; sustituye a la antiguaDOCKER_HOST_ADDRESS). SinJVB_ADVERTISE_IPS, el JVB intenta descubrir su IP pública por STUN; si la dirección anunciada es errónea, las llamadas fallan en cuanto se unen más de dos participantes.Activar Let's Encrypt en .env
Añada en
.env:ENABLE_LETSENCRYPT=1,LETSENCRYPT_DOMAIN=meet.sudominio.com,[email protected]. Compruebe que el puerto 80 está realmente abierto antes de arrancar — Let's Encrypt utiliza un desafío HTTP-01 que requiere una respuesta en ese puerto. El contenedor web obtiene y renueva el certificado con acme.sh (servicio de renovación interno) y lo guarda en~/.jitsi-meet-cfg/storage/web: conserve esa carpeta entre reinicios. Añada tambiénLETSENCRYPT_ACME_SERVER=letsencrypt: sin ella, acme.sh usa ZeroSSL por defecto.Configurar coturn (servidor TURN)
Es la etapa que la mayoría de las guías pasan por alto y la que hace fracasar la instalación. docker-jitsi-meet no incluye coturn: instálelo aparte (paquete o contenedor) y declárelo en
.envpara que Prosody entregue a los clientes credenciales temporales:TURNS_HOST=turn.sudominio.comyTURNS_PORTpara TLS, y ademásTURN_HOST=turn.sudominio.com,TURN_PORT=3478yTURN_TRANSPORT=udp,tcp. El puerto 443 en TLS permite atravesar los cortafuegos más restrictivos, que bloquean los puertos no estándar pero dejan pasar todo lo que se parece a HTTPS; exige una IP que el contenedor web no ocupe ya, si no, mantenga 5349.Genere después un secreto compartido (
openssl rand -hex 32) y colóquelo enTURN_CREDENTIALSen.envy en coturn (use-auth-secretystatic-auth-secret=<el mismo secreto>); nuncano-auth, que convertiría su servidor en un relé abierto a cualquiera. El certificado TLS se declara en la configuración de coturn (cert=ypkey=), no en.env. Si coturn se ejecuta en un contenedor separado, monte estos archivos como volumen.Último punto que a menudo se olvida: coturn no bloquea por defecto los rangos RFC1918 (192.168.x.x, 10.x.x.x, 172.16.x.x): solo rechaza el loopback. Un cliente autenticado puede, por tanto, retransmitir tráfico hacia su red interna. Añada
no-multicast-peersy líneasdenied-peer-ippara esos rangos y, si el JVB tiene una dirección interna, autorice solo esa conallowed-peer-ip.Arrancar la stack y comprobar el JVB
Ejecute
docker compose up -d. Espere 30 segundos, luego:docker compose logs jvb 2>&1 | grep -E 'register|connected|error'. Debe ver una línea que indique que el JVB se ha registrado en Jicofo. Si veFailed to registeroConnection refused, Prosody aún no está listo — vuelva a lanzarlo al cabo de un minuto. Compruebe después que los puertos responden:nc -zv meet.sudominio.com 443(web, TCP),nc -zv turn.sudominio.com 3478(coturn, TCP) ync -zvu meet.sudominio.com 10000(JVB, UDP; un éxito en UDP solo es orientativo).Primera prueba de video desde dos redes diferentes
Abra
https://meet.sudominio.comdesde su equipo habitual y desde un segundo dispositivo en una red diferente (un teléfono con datos móviles, por ejemplo). Si ambos participantes se ven y se oyen, el TURN funciona. Una prueba desde dos equipos en la misma red local no valida coturn — tiene éxito en modo punto a punto sin solicitar nunca el relé.
Configuración avanzada: grabación, autenticación y límites
Grabación de las reuniones. Jitsi integra Jibri para la grabación local o el streaming RTMP. Jibri captura el renderizado de un navegador en tiempo real: una instancia solo graba una reunión a la vez, y la documentación de Jitsi pide al menos 8 GB de RAM por instancia en 720p, preferiblemente en un servidor separado. Active ENABLE_RECORDING=1 en .env y arranque el contenedor Jibri incluido: docker compose -f docker-compose.yml -f jibri.yml up -d. Autenticación de los organizadores. Por defecto, cualquiera puede crear una sala: una instancia pública así configurada se convierte en videoconferencia gratuita para desconocidos, con su ancho de banda. Active ENABLE_AUTH=1, AUTH_TYPE=internal y ENABLE_GUESTS=1 para exigir una cuenta del lado del organizador dejando que los invitados se unan libremente. Cree las cuentas de anfitrión con docker compose exec prosody prosodyctl --config /run/prosody/config/prosody.cfg.lua register admin meet.jitsi <contrasena>: meet.jitsi es el dominio XMPP interno, no su nombre público, y la contraseña se genera (openssl rand -base64 18) en lugar de reutilizarse. Limitar los participantes anónimos. ENABLE_LOBBY=1 habilita la sala de espera: una vez que el moderador la activa, cada entrada debe validarse antes de acceder a la sala. Combinado con ENABLE_AUTH=1, esto da un control completo sobre quién accede a qué.
Endurecimiento: fail2ban en el servicio TURN
coturn expone un servicio accesible desde internet. Instale fail2ban y cree un filtro sobre los intentos de autenticación TURN fallidos: los fallos aparecen en el registro de coturn con un código 401; compruebe el formato exacto de su versión antes de escribir la regex del filtro. Una jail de fail2ban con maxretry=10 y bantime=600 basta para desalentar los escaneos automáticos sin afectar a sus usuarios legítimos. Fije el registro con log-file=/var/log/turnserver.log en la configuración de coturn (sin esta opción, coturn elige por sí mismo un archivo en /var/log, /var/tmp o /tmp), haga que fail2ban vigile esa ruta, o monte ese archivo desde el contenedor.
Solución de problemas: los cuatro errores más frecuentes
Sin sonido o video bloqueado en «connecting» — coturn + UDP 10000. Es la avería más frecuente. Compruebe en este orden: (1) ufw status — ¿están abiertos los puertos TCP 443 y UDP 10000? (2) los registros de coturn (journalctl -u coturn en nativo, docker logs <contenedor-coturn> si no) — ¿arranca coturn sin error de certificado? (3) Desde una máquina externa: curl -v telnet://meet.sudominio.com:3478 — ¿responde coturn? (4) En los logs del navegador (F12 → Consola): ¿ve ICE failed o failed to gather candidates? Si es así, coturn no es accesible desde el exterior. Compruebe que TURN_HOST y TURNS_HOST en .env resuelven a la IP real del servidor coturn. Conexión rechazada en TCP 443. Si el certificado Let's Encrypt no pudo emitirse (puerto 80 cerrado en el primer arranque), el contenedor web se detiene tras el mensaje Failed to obtain a certificate from the Let's Encrypt CA y ya nada responde en el 443. Solución: abra el puerto 80, compruebe HTTP_PORT=80 y vuelva a lanzar docker compose up -d; no hace falta borrar la configuración, y cada nuevo intento cuenta en los límites de Let's Encrypt. Compartir pantalla rechazado por el navegador. Compartir la pantalla exige un contexto seguro (HTTPS válido). Si ve getUserMedia is not supported, el certificado no es válido o el dominio no está en HTTPS. Compruebe PUBLIC_URL — debe empezar por https:// con un dominio cuyo certificado sea válido, no una IP en bruto. Error «ICE failed» persistente pese a un coturn operativo. Compruebe la variable JVB_ADVERTISE_IPS (antes DOCKER_HOST_ADDRESS): debe contener la IP pública del VPS, no la IP interna del contenedor Docker (172.17.x.x). Si su VPS está detrás de un NAT cloud (raro pero existente), necesita la IP NAT externa, no la IP de la interfaz de red.
Monitoreo: vigilar el ancho de banda y los participantes
El puente de video JVB expone una API de estadísticas en tiempo real en http://localhost:8080/colibri/stats. Este endpoint JSON informa del número de conferencias activas, participantes, ancho de banda entrante y saliente en kbit/s, y el RTT medio (rtt_aggregate). El puerto 8080 solo se publica en 127.0.0.1: manténgalo así. Para una vigilancia continua, el JVB ya expone sus métricas en formato Prometheus en /metrics (mismo puerto privado); del lado de Prosody, active PROSODY_ENABLE_METRICS=1 en .env. Conecte después una pila Prometheus + Grafana: el repositorio incluye prometheus.yml y grafana.yml, que publican los puertos 9090 y 3000 en todas las interfaces. Limítelos a 127.0.0.1 o a una VPN, porque los puertos publicados por Docker eluden las reglas de ufw.
Sin Prometheus, una vigilancia mínima se logra con un cron que consulta /colibri/stats y alerta cuando el rendimiento supera un umbral: curl -s http://127.0.0.1:8080/colibri/stats | python3 -c "import json,sys; s=json.load(sys.stdin); print(s.get('bit_rate_download',0), s.get('bit_rate_upload',0))".
Para estimar la capacidad antes de la saturación: la capacidad de un JVB depende ante todo del enlace de red y de las resoluciones enviadas; mídala con su propia carga en lugar de fiarse de una cifra genérica. Cuando el límite se acerca, rtt_aggregate sube y la calidad percibida cae antes de que la CPU sature — esa es la señal para añadir otra instancia JVB.
Su Jitsi está en marcha — gestione el ancho de banda
Jitsi funciona en modo SFU: el puente de video (JVB) retransmite cada flujo hacia cada participante, por lo que el ancho de banda saliente crece proporcionalmente al número de participantes multiplicado por el número de flujos. Un flujo 720p pesa unos 2,5 Mbit/s según la documentación de Jitsi: multiplique por el número de flujos realmente enviados a cada participante para estimar el caudal saliente. Fije channelLastN: 5 en custom-config.js (carpeta web de la configuración) para recibir solo los N últimos videos activos para limitar los flujos del lado del cliente en las reuniones grandes, y vigile docker stats jvb para anticipar la saturación. Por encima de una veintena de participantes simultáneos regulares, despliegue varias instancias JVB en VPS distintos detrás de un único Jicofo: es la arquitectura de escalado horizontal nativa de Jitsi. Para despliegues multi-organización o multidominio, tenga en cuenta que una instalación docker-jitsi-meet solo tiene una PUBLIC_URL: si cada organización necesita sus propias cuentas y un espacio de salas aislado, prevea una instancia por organización, en su propio subdominio.