Cómo configurar Nginx RTMP para streaming en vivo desde OBS

Guía técnica para instalar el módulo Nginx RTMP en Ubuntu, configurar la stream key, publicar desde OBS y generar HLS para distribución en navegador.

Un servidor de streaming propio significa control total: ingesta RTMP desde OBS, distribución en HLS para navegador, posibilidad de grabar todo en disco y cero dependencia de plataformas externas con reglas de contenido opacas. El módulo nginx-rtmp resuelve esto en una sola pieza — recibe el stream del encoder y lo empaqueta en fragmentos HLS dentro del mismo proceso.

Este tutorial cubre la instalación de Nginx con el módulo RTMP compilado desde el código fuente en Ubuntu 24.04 LTS, la configuración de una aplicación live con autenticación por clave, la generación del manifiesto HLS para distribución y las reglas de firewall para reducir la superficie de ataque. Al final, publicas desde OBS y reproduces en el navegador vía HTML5.

Tiempo estimado: 30-40 minutos en una VPS limpia. Coste de ejecución: ~80 MB de paquetes de build + ~30 MB del binario Nginx final.

Requisitos previos

Antes de empezar

Necesitas Ubuntu 24.04 LTS con acceso sudo, al menos 2 GB de RAM (la compilación consume ~1 GB en pico) y los puertos 80, 443 y 1935 abiertos en el firewall externo del proveedor. SSH conectado y funcionando.

Sistema operativo Ubuntu 24.04 LTS
Puerto RTMP 1935/tcp
Puerto HLS 80/tcp o 443/tcp
Encoder probado OBS Studio 30+

Paquetes que vamos a instalar para la compilación: build-essential, libpcre3-dev, libssl-dev, zlib1g-dev y git. Todos del repositorio oficial — ningún PPA externo.

Instalar dependencias de build

Antes de descargar el código fuente, prepara el sistema con las bibliotecas que Nginx necesita para compilar con soporte a SSL/TLS, regex y compresión gzip.

01

Actualiza el índice de paquetes de APT:

sudo apt update

Este comando sincroniza las listas de paquetes disponibles. Es necesario antes de instalar cualquier cosa nueva en un servidor que ha estado inactivo.

02

Instala el compilador y las bibliotecas de desarrollo:

sudo apt install -y build-essential libpcre3-dev libssl-dev zlib1g-dev git wget

build-essential trae gcc y make. libpcre3-dev es necesario para el módulo de regex de Nginx, libssl-dev para HTTPS y zlib1g-dev para gzip. Total de ~250 MB instalados.

Descargar el código fuente de Nginx y del módulo RTMP

El módulo nginx-rtmp-module se mantiene en un fork — el original de Arut está archivado desde 2017. Para producción en 2026 usa el fork arut/nginx-rtmp-module aún funcional para funciones clásicas, o sergey-dryabzhinsky/nginx-rtmp-module que sigue recibiendo parches.

03

Crea un directorio de trabajo y descarga Nginx estable:

mkdir -p ~/build && cd ~/build
wget https://nginx.org/download/nginx-1.26.2.tar.gz
tar -xzvf nginx-1.26.2.tar.gz

La versión 1.26.x es la línea estable actual. Evita la mainline (1.27.x) en producción a menos que necesites una característica específica.

04

Clona el módulo RTMP:

cd ~/build
git clone https://github.com/sergey-dryabzhinsky/nginx-rtmp-module.git

Este fork mantiene compatibilidad con Nginx moderno y tiene bugfixes que el original archivado no recibió.

Compilar e instalar Nginx con el módulo RTMP

La compilación lleva 4-5 minutos en una vCPU moderna. El configure acepta decenas de flags — abajo mantenemos el conjunto mínimo necesario para un servidor de streaming HTTPS funcional.

05

Configura el build apuntando al módulo RTMP:

cd ~/build/nginx-1.26.2
./configure \
  --prefix=/etc/nginx \
  --sbin-path=/usr/sbin/nginx \
  --conf-path=/etc/nginx/nginx.conf \
  --pid-path=/var/run/nginx.pid \
  --with-http_ssl_module \
  --with-http_v2_module \
  --add-module=../nginx-rtmp-module

Si algún --with-* se queja por una biblioteca faltante, instala el -dev correspondiente vía apt y vuelve a ejecutar configure.

06

Compila e instala:

make -j$(nproc)
sudo make install

-j$(nproc) paraleliza la compilación en el número de núcleos disponibles. En una vCPU única lleva ~7 minutos; en 4 vCPUs, ~2 minutos.

07

Crea el servicio systemd para gestionar Nginx:

sudo tee /etc/systemd/system/nginx.service > /dev/null <<'EOF'
[Unit]
Description=Nginx HTTP and RTMP server
After=network.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/usr/sbin/nginx -s quit
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable nginx

ExecStartPre valida la configuración antes de arrancar — evita un servicio roto tras una edición.

Configurar la aplicación RTMP live

Ahora editamos nginx.conf para añadir el bloque rtmp (fuera del bloque http, en el nivel raíz) y configurar una aplicación live que acepta la publicación de OBS y genera fragmentos HLS.

08

Crea el directorio donde se escribirán los fragmentos HLS:

sudo mkdir -p /var/www/hls
sudo chown -R nobody:nogroup /var/www/hls

Nginx, sin un usuario personalizado configurado, se ejecuta como nobody. Ajusta esto si defines user www-data en el nginx.conf.

09

Sustituye /etc/nginx/nginx.conf por una configuración que combine HTTP + RTMP:

worker_processes auto;
events {
    worker_connections 1024;
}

rtmp {
    server {
        listen 1935;
        chunk_size 4096;

        application live {
            live on;
            record off;

            hls on;
            hls_path /var/www/hls;
            hls_fragment 3s;
            hls_playlist_length 60s;

            allow publish 127.0.0.1;
            allow publish 198.51.100.0/24;
            deny publish all;
        }
    }
}

http {
    include mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;

    server {
        listen 80;
        server_name _;

        location /hls {
            types {
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }
            root /var/www;
            add_header Cache-Control no-cache;
            add_header Access-Control-Allow-Origin *;
        }

        location /stat {
            rtmp_stat all;
            rtmp_stat_stylesheet stat.xsl;
        }

        location /stat.xsl {
            root /var/www;
        }
    }
}

Sustituye 198.51.100.0/24 por tu IP/red de origen real. El bloque allow publish es la primera capa de defensa — sin él, cualquier persona con tu URL RTMP podría publicar.

El bloque rtmp va fuera del http

Error clásico: poner el bloque rtmp dentro de http {}. Es top-level, paralelo a http. Si lo pegas dentro, Nginx fallará en nginx -t con un mensaje confuso sobre unknown directive.

10

Valida la sintaxis y arranca el servicio:

sudo nginx -t
sudo systemctl start nginx

nginx -t debería responder configuration file ... test is successful. Cualquier error aquí indica la línea exacta — corrígelo antes de continuar.

Abrir puertos en el firewall

Sin reglas de firewall, el puerto 1935 queda expuesto a todo Internet. UFW en Ubuntu lo controla de forma sencilla.

11

Habilita UFW y abre los puertos necesarios:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 1935/tcp
sudo ufw enable

Confirma con sudo ufw status numbered que aparecen las 4 reglas antes de desconectar del SSH.

No olvides SSH antes del enable

ufw enable aplica el default de denegar todo el tráfico entrante inmediatamente. Si olvidas abrir OpenSSH primero, pierdes la sesión y necesitas acceder por la consola serie del proveedor para desbloquearlo.

Publicar desde OBS

Con el servidor listo, configura OBS para la ingesta.

12

Abre OBS → Configuración → Emisión y completa:

Servicio Personalizado
Servidor rtmp://TU-IP/live
Clave de transmisión mi-stream-001

La “clave” es el nombre (name) que aparece en los fragmentos HLS y en los logs de Nginx-RTMP. Puede ser cualquier cadena ASCII sin espacios.

13

Haz clic en “Iniciar transmisión” en OBS. OBS conecta a rtmp://TU-IP/live y empieza a publicar el stream con el nombre mi-stream-001.

Verificar la transmisión

Comprueba que los fragmentos HLS se están generando y reproducen en un reproductor web.

14

Lista los archivos en /var/www/hls:

ls -la /var/www/hls/

Salida esperada, ~3 segundos después de empezar a publicar:

mi-stream-001-0.ts
mi-stream-001-1.ts
mi-stream-001.m3u8

Los .ts son fragmentos MPEG-TS de ~3 segundos cada uno. El .m3u8 es el manifiesto que apunta a los fragmentos actuales.

15

Prueba en el navegador vía https://hls-js.netlify.app/demo/ o cualquier reproductor que acepte HLS. Pega http://TU-IP/hls/mi-stream-001.m3u8 y dale play.

Para una prueba rápida sin reproductor web, usa ffplay:

ffplay http://TU-IP/hls/mi-stream-001.m3u8

La latencia esperada de OBS al reproductor es de 9-15 segundos con la configuración por defecto de arriba (hls_fragment 3s × buffer de 3 fragmentos en el reproductor).

Resolución de problemas

OBS conecta pero no aparece nada en /var/www/hls

Verifica el permiso del directorio:

sudo -u nobody touch /var/www/hls/test && rm /var/www/hls/test

Si responde “Permission denied”, vuelve a aplicar chown -R nobody:nogroup /var/www/hls.

Error “Failed to connect to server” en OBS

Comprueba si el puerto 1935 está abierto desde fuera:

nc -zv TU-IP 1935

Si falla, revisa UFW (sudo ufw status) y el firewall del proveedor de la VPS — algunos tienen un firewall externo separado.

El reproductor carga el m3u8 pero da “no supported source”

Falta CORS o el MIME type es incorrecto. Confirma que la respuesta del .m3u8 trae Content-Type: application/vnd.apple.mpegurl y Access-Control-Allow-Origin: *:

curl -I http://TU-IP/hls/mi-stream-001.m3u8

Próximos pasos

Ya tienes un servidor RTMP funcional convirtiendo a HLS. Desde aquí puedes profundizar en varias direcciones:

  • Añadir HTTPS con Let’s Encrypt en el servidor HTTP (Certbot funciona con normalidad — el RTMP en sí no recibe TLS directo, pero el HLS sí).
  • Implementar autenticación vía on_publish apuntando a un endpoint propio que valide tokens contra la base de datos.
  • Habilitar grabación automática con record all + record_path /var/www/recordings para archivar todo en FLV.
  • Transcodificar a múltiples bitrates con exec ffmpeg dentro del bloque application — necesario para un ABR (adaptive bitrate) decente.
  • Cambiar HLS por LL-HLS (hls_fragment 1s) o WebRTC si necesitas latencia sub-segundo.

Si vas a llevar esto a producción, una VPS Hostini de streaming ya viene con ancho de banda dedicado y los puertos RTMP/1935 abiertos por defecto — sin necesidad de abrir un ticket para el firewall externo.

Preguntas frecuentes

¿Por qué necesito compilar Nginx desde cero en lugar de usar el paquete de apt?

El módulo nginx-rtmp no es dinámico en el Nginx estable de Ubuntu — debe enlazarse en tiempo de compilación mediante `--add-module`. El paquete `libnginx-mod-rtmp` existe en algunos mirrors, pero suele quedar bloqueado en versiones antiguas del módulo (1.2.1 de 2017). Compilar lleva 4-5 minutos y te da control total de la versión.

¿Cuál es la latencia real de un stream RTMP convertido a HLS?

Entre 8 y 30 segundos con la configuración por defecto, dependiendo de `hls_fragment` (por defecto 5s) y `hls_playlist_length`. Para reducirla por debajo de 5s, considera LL-HLS con `hls_fragment 1s` y un reproductor compatible (hls.js 1.x), o cambia a WebRTC si necesitas sub-segundo.

¿Puedo transmitir desde OBS directamente a HLS sin RTMP?

No de forma nativa. OBS exporta en RTMP, SRT o WHIP (WebRTC). HLS es un formato de distribución para reproductor, no de ingesta. El flujo estándar es OBS → RTMP → Nginx-RTMP genera HLS → el reproductor consume HLS por HTTP.

¿Cómo restrinjo quién puede publicar en el stream sin exponer la clave?

Usa `on_publish` apuntando a un endpoint HTTP propio que valide el `name` (stream key) contra la base de datos. Nginx-RTMP solo permite la publicación si el endpoint responde 2xx. Combínalo con un firewall que cierre el puerto 1935 a IPs autorizadas si la base de transmisores es fija.

¿Cuántos streams simultáneos aguanta un servidor medio?

Depende de la CPU y el ancho de banda. En una VPS de 4 vCPU + 8 GB sin transcodificación (solo retransmitiendo RTMP y generando HLS por fragmento de copia directa), atiende 50-100 publishers concurrentes. Con transcodificación H.264 vía ffmpeg para múltiples bitrates, baja a 4-8 streams por CPU.

Los fragmentos HLS se generan pero el reproductor no consigue reproducir — ¿qué revisar?

Tres causas comunes: 1) CORS — añade `add_header 'Access-Control-Allow-Origin' '*'` en el location HLS; 2) MIME type — confirma que `.m3u8` es `application/vnd.apple.mpegurl` y `.ts` es `video/mp2t`; 3) permisos del directorio — `/var/www/hls` debe ser escribible por el usuario de Nginx (normalmente www-data).

Temas:
Próximos pasos Infraestructura optimizada para transmisión en vivo, grabación y VOD.Monta tu servidor de streaming en Hostini →
¿Te resultó útil este tutorial?
Hablar por WhatsApp