El módulo RTMP de Nginx convierte un servidor web en un servidor de streaming: recibe la emisión de un programa como OBS Studio por el protocolo RTMP y la reparte a los espectadores, ya sea por RTMP o en formato HLS, que se reproduce en cualquier navegador. En este tutorial instalarás Nginx con el módulo RTMP en Ubuntu 24.04, protegerás la emisión con una clave, publicarás el directo en HLS con un reproductor web, y opcionalmente lo grabarás y lo retransmitirás a YouTube o Twitch.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. Para reenviar el vídeo sin transcodificar bastan 2 vCPUs y 2 GB de RAM; lo que más importa es el ancho de banda de salida.
- Un usuario no root con privilegios
sudo. - OBS Studio (u otro software compatible con RTMP) en tu ordenador para emitir.
- Opcional: un dominio con un registro A apuntando a la IP del servidor, si quieres servir el directo por HTTPS.
Calcula el ancho de banda: cada espectador consume aproximadamente el bitrate de la emisión. Con 3 Mbit/s y 100 espectadores simultáneos necesitas unos 300 Mbit/s de salida.
Paso 1: Instalar Nginx y el módulo RTMP
Ubuntu empaqueta el módulo RTMP como módulo dinámico de Nginx, así que no hace falta compilar nada. Instala ambos, junto con FFmpeg, que usarás para enviar una emisión de prueba:
sudo apt update
sudo apt install nginx libnginx-mod-rtmp ffmpeg
El paquete activa el módulo automáticamente. Compruébalo:
ls /etc/nginx/modules-enabled/ | grep rtmp
50-mod-rtmp.conf
Paso 2: Crear los directorios de trabajo
El módulo escribe los fragmentos HLS en disco y Nginx los sirve por HTTP. Los procesos de Nginx en Ubuntu se ejecutan como www-data, así que ese usuario necesita permiso de escritura:
sudo mkdir -p /var/www/stream/hls
sudo chown -R www-data:www-data /var/www/stream
Paso 3: Configurar el servidor RTMP
La configuración RTMP no va dentro del bloque http, sino en un bloque rtmp propio en el nivel superior de la configuración. Créala en un archivo aparte:
sudo nano /etc/nginx/rtmp.conf
rtmp {
server {
listen 1935;
chunk_size 4096;
notify_method get;
application live {
live on;
record off;
# Comprobar la clave de emisión antes de aceptar la publicación
on_publish http://127.0.0.1:8080/auth;
# Los espectadores ven el directo por HLS, no por RTMP
allow play 127.0.0.1;
deny play all;
hls on;
hls_path /var/www/stream/hls;
hls_fragment 3s;
hls_playlist_length 60s;
}
}
}
Qué hace cada directiva:
listen 1935: puerto estándar de RTMP.application live: define la aplicación a la que se emite. La URL de emisión serártmp://your_server_ip/live.on_publish: antes de aceptar una emisión, Nginx hace una petición HTTP a esa URL. Si responde con un código 2xx, la emisión se acepta; si no, se rechaza.notify_method gethace que la petición sea GET, para poder leer sus parámetros desde Nginx.hls_fragmentyhls_playlist_length: duración de cada fragmento y del historial de la lista de reproducción. Fragmentos más cortos reducen el retraso, pero exigen más peticiones a los espectadores.
Añade el archivo al final de /etc/nginx/nginx.conf, fuera del bloque http:
echo 'include /etc/nginx/rtmp.conf;' | sudo tee -a /etc/nginx/nginx.conf
Paso 4: Configurar la entrega HLS y la clave de emisión
Ahora configura la parte HTTP. Necesitas dos bloques server: uno público en el puerto 80 que sirve los fragmentos HLS y el reproductor, y otro que solo escucha en 127.0.0.1:8080 con la comprobación de la clave y la página de estadísticas, inaccesible desde fuera.
Elige una clave de emisión larga y aleatoria:
openssl rand -hex 16
3f9c1e7a52b84d06a1c9e2f7b3d58a40
Crea el sitio:
sudo nano /etc/nginx/sites-available/stream
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name your_domain;
root /var/www/stream;
index index.html;
location /hls/ {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
add_header Cache-Control no-cache;
add_header Access-Control-Allow-Origin *;
}
}
server {
listen 127.0.0.1:8080;
location = /auth {
if ($arg_key = "your_stream_key") {
return 200;
}
return 403;
}
location = /stat {
rtmp_stat all;
}
}
Sustituye your_stream_key por la clave que generaste y your_domain por tu dominio (o por la IP del servidor si no tienes dominio). Con notify_method get, los parámetros que añada el emisor a la URL (?key=...) llegan a /auth y Nginx los lee en $arg_key.
Desactiva el sitio por defecto, activa el nuevo y comprueba la sintaxis:
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/stream /etc/nginx/sites-enabled/stream
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Recarga Nginx y comprueba que escucha en los puertos 1935, 80 y 8080:
sudo systemctl reload nginx
sudo ss -tlnp | grep nginx
LISTEN 0 511 127.0.0.1:8080 0.0.0.0:* users:(("nginx",pid=4122,fd=8),...)
LISTEN 0 511 0.0.0.0:1935 0.0.0.0:* users:(("nginx",pid=4122,fd=6),...)
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=4122,fd=7),...)
LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=4122,fd=9),...)
Paso 5: Abrir los puertos en el firewall
Abre el puerto RTMP para recibir emisiones y el HTTP para los espectadores. Permite SSH antes de activar UFW para no perder el acceso:
sudo ufw allow OpenSSH
sudo ufw allow 1935/tcp
sudo ufw allow 'Nginx HTTP'
sudo ufw enable
Si siempre emites desde la misma IP fija, puedes limitar el puerto RTMP a ella con sudo ufw allow from your_home_ip to any port 1935 proto tcp en lugar de abrirlo a todo el mundo.
Paso 6: Probar con una emisión generada por FFmpeg
Antes de configurar OBS, comprueba que todo funciona con una emisión de prueba generada en el propio servidor: una carta de ajuste en movimiento con un tono de audio. Sustituye your_stream_key por tu clave:
ffmpeg -re \
-f lavfi -i testsrc2=size=1280x720:rate=30 \
-f lavfi -i sine=frequency=1000:sample_rate=48000 \
-c:v libx264 -preset veryfast -b:v 2500k -g 60 -pix_fmt yuv420p \
-c:a aac -b:a 128k \
-f flv "rtmp://127.0.0.1/live/stream?key=your_stream_key"
-g 60 fuerza un fotograma clave cada 2 segundos (a 30 fps), necesario para que los fragmentos HLS se corten con regularidad. Deja el comando en marcha y, en otra terminal, comprueba que se generan los fragmentos:
ls /var/www/stream/hls/
stream-0.ts stream-1.ts stream-2.ts stream.m3u8
El nombre de los archivos es el nombre del stream (stream); la clave no forma parte de él y nunca aparece en la URL de los espectadores. Consulta la lista de reproducción por HTTP:
curl http://127.0.0.1/hls/stream.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:4
#EXTINF:4.000,
stream-0.ts
#EXTINF:4.000,
stream-1.ts
Comprueba también que la clave funciona: repite el comando ffmpeg con una clave incorrecta. La conexión se rechazará y en /var/log/nginx/error.log verás que la notificación on_publish ha devuelto un error. Detén FFmpeg con Ctrl+C.
Paso 7: Añadir un reproductor web
Los navegadores basados en Chromium y Firefox no reproducen HLS de forma nativa, así que el reproductor usa la librería hls.js (Safari lo reproduce directamente). Crea la página:
sudo nano /var/www/stream/index.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Directo</title>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
</head>
<body>
<video id="player" controls muted autoplay playsinline style="width: 100%; max-width: 960px;"></video>
<script>
const video = document.getElementById('player');
const src = '/hls/stream.m3u8';
if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = src;
} else if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(src);
hls.attachMedia(video);
}
</script>
</body>
</html>
Ajusta los permisos:
sudo chown www-data:www-data /var/www/stream/index.html
Vuelve a lanzar la emisión de prueba del paso 6 y abre http://your_server_ip/ en el navegador. Deberías ver la carta de ajuste con un retraso de 10 a 20 segundos, lo normal en HLS.
Paso 8: Emitir desde OBS Studio
En OBS, ve a Ajustes > Emisión y configura:
| Campo | Valor |
|---|---|
| Servicio | Personalizado |
| Servidor | rtmp://your_server_ip/live |
| Clave de retransmisión | stream?key=your_stream_key |
En Ajustes > Salida, con el modo de salida en Avanzado, fija el Intervalo de fotogramas clave en 2 segundos y un bitrate de vídeo acorde a tu conexión de subida (por ejemplo, 2500-4500 kbps para 720p o 1080p a 30 fps). Pulsa Iniciar transmisión y abre http://your_server_ip/ para ver el directo.
Para ver qué emisiones están activas y cuántos clientes tienen, consulta las estadísticas desde el servidor:
curl -s http://127.0.0.1:8080/stat | grep -E '<name>|<nclients>'
<name>live</name>
<name>stream</name>
<nclients>1</nclients>
Paso 9 (opcional): Retransmitir a YouTube o Twitch
Nginx puede reenviar la emisión que recibe a otras plataformas, de modo que desde OBS emites una sola vez y el servidor la reparte. Añade una directiva push por destino dentro de application live, en /etc/nginx/rtmp.conf:
application live {
# ... configuración anterior ...
push rtmp://a.rtmp.youtube.com/live2/your_youtube_key;
push rtmp://live.twitch.tv/app/your_twitch_key;
}
Las claves son las que te da cada plataforma en su panel de emisión. Comprueba la configuración y recarga:
sudo nginx -t && sudo systemctl reload nginx
La retransmisión solo reenvía el vídeo, sin transcodificar, así que la emisión de OBS debe cumplir los requisitos de cada plataforma (H.264, AAC, fotograma clave cada 2 segundos).
Paso 10 (opcional): Grabar las emisiones
Para guardar cada emisión en disco, crea un directorio de grabaciones:
sudo mkdir -p /var/www/recordings
sudo chown www-data:www-data /var/www/recordings
Y en application live, sustituye record off; por:
record all;
record_path /var/www/recordings;
record_unique on;
record_unique on añade la fecha al nombre del archivo para no sobrescribir grabaciones anteriores. Tras recargar Nginx, cada emisión se guarda en formato FLV. Para convertirla a MP4 sin recodificar:
ffmpeg -i /var/www/recordings/stream-1758795600.flv -c copy stream.mp4
Las grabaciones ocupan aproximadamente el bitrate por la duración (unos 1,3 GB por hora a 3 Mbit/s), así que vigila el espacio en disco con df -h.
Solución de problemas
OBS muestra "No se pudo conectar al servidor": comprueba que el puerto 1935 está abierto (sudo ufw status) y que Nginx escucha en él (sudo ss -tlnp | grep 1935). Si ambos están bien, revisa la clave: una clave incorrecta también cierra la conexión.
nginx: [emerg] unknown directive "rtmp": el módulo no está cargado. Comprueba que existe /etc/nginx/modules-enabled/50-mod-rtmp.conf y que la línea include /etc/nginx/rtmp.conf; está al final de nginx.conf y no dentro del bloque http.
No aparecen archivos en /var/www/stream/hls: busca errores de permisos con sudo tail -n 50 /var/log/nginx/error.log y comprueba que el directorio pertenece a www-data.
El vídeo se corta o se congela en el navegador: suele deberse a un intervalo de fotogramas clave distinto de 2 segundos o a un bitrate superior a la subida de tu conexión. Revisa las estadísticas de OBS (fotogramas perdidos por red).
Conclusión
Tienes un servidor de streaming con Nginx que recibe emisiones RTMP protegidas con clave, las publica en HLS con un reproductor web y puede retransmitirlas a otras plataformas y grabarlas. Como siguientes pasos, protege el reproductor con HTTPS usando Certbot (sudo apt install certbot python3-certbot-nginx), transcodifica la emisión a varias calidades con FFmpeg para adaptarte a espectadores con conexiones lentas, o coloca una CDN delante de /hls/ si esperas muchos espectadores simultáneos.
