Auto-hospeda Jitsi Meet para videollamadas
Sigue esto en tu propio servidor. Empieza gratis, sin tarjeta
Zoom y Google Meet limitan las llamadas gratuitas por tiempo o número de participantes, y los planes pagos cobran por anfitrión, por mes, los uses o no ese mes. Jitsi Meet es videoconferencia de código abierto que hace el mismo trabajo central (llamadas en el navegador con compartir pantalla, sin instalar nada) como software autoalojado: cuatro contenedores, un dominio, y ninguna factura por minuto o por asiento una vez que está corriendo. Esta guía lo monta desde un servidor vacío usando las imágenes oficiales, con Caddy manejando el HTTPS al frente, y explica la única pieza (un solo puerto UDP) que determina si las llamadas realmente conectan cuando más de una persona está detrás de una red distinta.
Al final tendrás una instancia de Jitsi Meet funcionando en tu propio dominio, entenderás por qué importa ese puerto UDP, y conocerás el estado honesto del atajo en un clic de Railyard para esta app en particular.
Lo que necesitas
- Un servidor con margen real. Una llamada de prueba entre dos personas en una red local cabe en un Hetzner CX23 (2 vCPU, 4 GB de RAM, €5.99/mes sin IVA), pero reuniones de equipo sostenidas quieren más: un CX33 (4 vCPU, 8 GB de RAM, €8.99/mes) es una base más segura una vez que varias personas dependen de esto a diario.
- Un dominio con un registro DNS A apuntando al servidor (esta guía usa
meet.example.com). - La dirección IPv4 pública del servidor, conocida de antemano, no solo asignada (la necesitarás para la traversía de NAT en el paso 3).
- Acceso SSH y unos 20 minutos.
Paso 1: aprovisiona el servidor e instala Docker
Crea el servidor con Ubuntu 24.04, apunta el DNS hacia él, e instala Docker:
ssh root@203.0.113.30
apt update && apt upgrade -y
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
docker --version
docker compose version
Paso 2: obtén los directorios de configuración oficiales
Los documentos de Jitsi dirigen a descargar una versión etiquetada en vez de clonar el repositorio directamente, ya que el archivo compose en la rama por defecto puede ir adelante de lo que las imágenes publicadas realmente soportan. Esta guía usa la versión actual, stable-11248-1:
mkdir -p ~/.jitsi-meet-cfg/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb}
chmod 777 ~/.jitsi-meet-cfg/transcripts
Estos directorios guardan la configuración que Jitsi genera por sí mismo y persisten entre actualizaciones; perderlos significa regenerar contraseñas y cualquier personalización.
Paso 3: escribe el archivo de entorno
CONFIG=~/.jitsi-meet-cfg
HTTP_PORT=8000
HTTPS_PORT=8443
JVB_PORT=10000
PUBLIC_URL=https://meet.example.com
DISABLE_HTTPS=1
ENABLE_LETSENCRYPT=0
ENABLE_HTTP_REDIRECT=0
XMPP_DOMAIN=meet.jitsi
XMPP_AUTH_DOMAIN=auth.meet.jitsi
XMPP_MUC_DOMAIN=muc.meet.jitsi
XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi
XMPP_GUEST_DOMAIN=guest.meet.jitsi
XMPP_SERVER=xmpp.meet.jitsi
JICOFO_COMPONENT_SECRET=
JICOFO_AUTH_PASSWORD=
JVB_AUTH_PASSWORD=
JVB_ADVERTISE_IPS=203.0.113.30
ENABLE_GUESTS=1
TZ=Etc/UTC
Genera los tres secretos vacíos con:
for v in JICOFO_COMPONENT_SECRET JICOFO_AUTH_PASSWORD JVB_AUTH_PASSWORD; do
echo "$v=$(openssl rand -hex 16)"
done
Pega cada valor en .env en lugar del vacío. DISABLE_HTTPS=1 y ENABLE_LETSENCRYPT=0 importan específicamente aquí porque es Caddy quien maneja el HTTPS, no la opción integrada del contenedor web, que de otro modo pelearía con Caddy por el puerto 443. JVB_ADVERTISE_IPS es el ajuste que la mayoría de las guías de auto-hospedaje de Jitsi entierran: es la IP pública del servidor, dicha explícitamente al videobridge para que pueda anunciar una dirección alcanzable a los participantes detrás de un NAT en vez de solo su dirección de red Docker privada.
Paso 4: escribe el archivo Docker Compose
services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy-data:/data
- caddy-config:/config
depends_on:
- web
web:
image: ghcr.io/jitsi/web:stable-11248-1
restart: unless-stopped
volumes:
- ${CONFIG}/web:/config
- ${CONFIG}/transcripts:/usr/share/jitsi-meet/transcripts
env_file:
- .env
depends_on:
- jvb
prosody:
image: ghcr.io/jitsi/prosody:stable-11248-1
restart: unless-stopped
expose:
- "5222"
- "5269"
- "5347"
- "5280"
volumes:
- ${CONFIG}/prosody/config:/config
- ${CONFIG}/prosody/prosody-plugins-custom:/prosody-plugins-custom
env_file:
- .env
jicofo:
image: ghcr.io/jitsi/jicofo:stable-11248-1
restart: unless-stopped
volumes:
- ${CONFIG}/jicofo:/config
env_file:
- .env
depends_on:
- prosody
jvb:
image: ghcr.io/jitsi/jvb:stable-11248-1
restart: unless-stopped
ports:
- "${JVB_PORT:-10000}:${JVB_PORT:-10000}/udp"
volumes:
- ${CONFIG}/jvb:/config
env_file:
- .env
depends_on:
- prosody
volumes:
caddy-data:
caddy-config:
El servicio jvb es el que tiene un puerto de host real publicado directamente, 10000/udp, en vez de escondido detrás de Caddy. Eso no es una simplificación; el HTTPS se puede pasar por un proxy, pero el flujo real de audio y video no, porque WebRTC necesita una ruta UDP directa. Valida antes de levantar:
docker compose config
Paso 5: escribe el Caddyfile
meet.example.com {
reverse_proxy web:8000
}
Paso 6: levanta todo y abre el firewall
docker compose up -d
docker compose ps
Deberías ver los cinco servicios (caddy, web, prosody, jicofo, jvb) como running. Abre el puerto UDP explícitamente si el servidor tiene un firewall activo:
ufw allow 10000/udp
ufw allow 80/tcp
ufw allow 443/tcp
Visita https://meet.example.com; deberías ver la página de bienvenida de Jitsi Meet con un certificado válido. Inicia una reunión y únete desde dos redes distintas, no dos pestañas en la misma máquina, ya que probar en la misma red no revelará un problema de NAT o firewall con el puerto UDP.
El atajo: hazlo con Railyard, con honestidad
Railyard tiene una plantilla de stack para Jitsi Meet que aprovisiona los cuatro contenedores de la aplicación (web, prosody, jicofo, jvb) juntos: elige Jitsi Meet en el catálogo de plantillas. Vale la pena ser directo sobre dónde está hoy: el manifiesto de despliegue de Railyard todavía no tiene forma de publicar un puerto UDP ni de anunciar la IP pública de un servidor a un contenedor, como sí lo hacen JVB_ADVERTISE_IPS y el mapeo de puerto UDP del host en el paso 4. Los contenedores arrancan y la interfaz web carga, pero las llamadas entre participantes en redes distintas no tendrán una ruta de medios funcional hasta que eso se resuelva. Si necesitas que Jitsi Meet maneje llamadas de verdad hoy, los pasos 1 a 6 de arriba son el camino real; la plantilla de Railyard se sigue como una carencia conocida en vez de ocultarse.
Solución de problemas
La página carga pero las llamadas nunca conectan entre dos redes distintas. Esto casi siempre es JVB_ADVERTISE_IPS sin definir, o definido con la dirección privada del servidor en vez de la pública, combinado con el puerto UDP no realmente abierto en el firewall. Confirma ambos; una llamada entre dos pestañas del navegador en la misma máquina funcionará incluso con esto roto, por eso no es una prueba válida.
El HTTPS no carga, o Caddy registra un conflicto de puerto. Revisa que DISABLE_HTTPS=1 y ENABLE_LETSENCRYPT=0 estén realmente definidos en .env; sin ellos, el nginx integrado del contenedor web también intenta enlazar el puerto 443.
Un participante tiene audio pero no video, o al revés. Usualmente una red restrictiva del lado de ese participante (firewall corporativo, wifi de hotel) más que el servidor; pídeles que prueben desde otra red antes de asumir que el servidor está mal configurado.
Falta de memoria con más de un puñado de participantes. El videobridge de Jitsi enruta el medio para cada participante, y eso es trabajo real de CPU y memoria que crece con el número de personas, no un costo fijo. docker stats jvb mientras corre una llamada muestra si esa es la limitante; un CX33 o más grande tiene más margen que un CX23.
Los cambios en .env no surten efecto. La mayoría de las variables de entorno de Jitsi solo se leen una vez, al arrancar un contenedor; docker compose up -d después de un cambio en .env no siempre recrea un contenedor que ya está corriendo sin cambios. Fuérzalo con docker compose up -d --force-recreate.
Preguntas frecuentes
¿Puedo grabar reuniones? Sí, a través de Jibri, un componente de grabación separado que esta guía no configura; necesita su propio contenedor y más recursos de servidor, y vale la pena añadirlo solo una vez que la configuración base de arriba esté confirmada funcionando.
¿Cuántas personas puede manejar un servidor? Depende más de la calidad de video y el tamaño del servidor que de un número fijo; un CX33 maneja cómodamente las reuniones regulares de un equipo pequeño, y el CPU del contenedor jvb es lo que hay que vigilar conforme crece el número de participantes.
¿Necesito TURN para que esto funcione? La mayoría de las veces no, ya que JVB_ADVERTISE_IPS más un puerto UDP abierto cubre el caso común. Un servidor TURN ayuda específicamente cuando un participante está detrás de un firewall que bloquea el UDP directo por completo, lo cual es una porción más pequeña de las redes reales que antes.
¿Jitsi Meet es realmente gratis de correr? El software sí; el servidor y su ancho de banda son el costo real, y las videollamadas usan considerablemente más ancho de banda que una app web típica, algo que vale la pena considerar si un proveedor mide de cerca el tráfico saliente.
¿Cómo es la experiencia móvil? Jitsi tiene apps nativas de iOS y Android que funcionan con cualquier servidor autoalojado; apúntalas a https://meet.example.com igual que la versión de navegador.
¿Puedo correr esto detrás de un router NAT en casa en vez de un servidor en la nube? Sí, con un paso extra: redirige tanto el puerto UDP de medios como el TCP 443 del router hacia la máquina que corre Docker, y configura JVB_ADVERTISE_IPS con la IP pública del router mismo en vez de la dirección local de la máquina, ya que eso es lo que el videobridge necesita entregar a los participantes remotos.
Jitsi Meet es una de las historias de auto-hospedaje más honestas aquí precisamente porque lo único que de verdad importa, que una llamada conecte entre dos redes, se reduce a un puerto UDP y una dirección IP bien configurados, no un misterio. Configura bien esas dos cosas y el resto son cuatro contenedores y un proxy inverso, corriendo llamadas sin que nadie cuente los minutos.
Por qué eligen Railyard para esto
- Tu propia cuenta en la nube. Despliega en DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, o cualquier servidor Linux por SSH, facturado directamente por tu proveedor, sin márgenes adicionales.
- Un precio fijo por equipo. El plan Free es gratis para siempre: un servidor más un servidor de base de datos, tres apps, sin tarjeta. Pro cuesta $19 al mes por hasta cinco servidores y apps ilimitadas. Team cuesta $49 al mes con servidores ilimitados. Sin cargos por app, por despliegue, por asiento ni por gigabyte.
- Plantillas de un clic. Despliega apps de código abierto, como bases de datos, analítica y herramientas de desarrollo, en tu propio servidor con un clic.
- Bases de datos administradas. Postgres, MySQL y Redis, con copias de seguridad programadas y verificadas por restauración, y recuperación a un punto en el tiempo.
- Despliegue con git push. Despliega desde GitHub, GitLab o la CLI, con HTTPS automático y lanzamientos sin tiempo de inactividad.