Como configurar Nginx RTMP para streaming live a partir do OBS
Guia técnico pra instalar o módulo Nginx RTMP no Ubuntu, configurar a stream key, publicar do OBS e gerar HLS pra distribuição em navegador.
Servidor de streaming próprio significa controle total: ingestão RTMP do OBS,
distribuição em HLS pra navegador, possibilidade de gravar tudo em disco e
zero dependência de plataformas terceiras com regras de conteúdo opacas. O
módulo nginx-rtmp resolve isso em uma única peça — recebe a stream do
encoder e empacota em fragmentos HLS no mesmo processo.
Este tutorial cobre a instalação do Nginx com o módulo RTMP compilado a
partir do código fonte no Ubuntu 24.04 LTS, configuração de uma aplicação
live com autenticação por chave, geração de manifesto HLS pra
distribuição e regras de firewall pra fechar a superfície de ataque. No
final, você publica do OBS e assiste no navegador via HTML5.
Tempo estimado: 30-40 minutos numa VPS limpa. Custo de execução: ~80 MB de pacotes de build + ~30 MB do binário Nginx final.
Pré-requisitos
Você precisa de Ubuntu 24.04 LTS com acesso sudo, ao menos 2 GB de RAM (a compilação consome ~1 GB em pico) e portas 80, 443 e 1935 liberadas no firewall externo do provedor. SSH conectado e funcionando.
Ubuntu 24.04 LTS 1935/tcp 80/tcp ou 443/tcp OBS Studio 30+ Pacotes que vamos instalar pra compilação: build-essential, libpcre3-dev,
libssl-dev, zlib1g-dev e git. Todos do repositório oficial — nenhum
PPA externo.
Instalar dependências de build
Antes de baixar o código fonte, prepare o sistema com as bibliotecas que o Nginx precisa pra compilar com suporte a SSL/TLS, regex e compressão gzip.
Atualize o índice de pacotes do APT:
sudo apt updateEsse comando sincroniza as listas de pacotes disponíveis. Necessário antes de instalar qualquer coisa nova num servidor que ficou parado.
Instale o compilador e as bibliotecas de desenvolvimento:
sudo apt install -y build-essential libpcre3-dev libssl-dev zlib1g-dev git wgetbuild-essential traz gcc e make. libpcre3-dev é necessário pro módulo
de regex do Nginx, libssl-dev pro HTTPS e zlib1g-dev pra gzip. Total
de ~250 MB instalados.
Baixar o código fonte do Nginx e do módulo RTMP
O módulo nginx-rtmp-module é mantido em fork — o original do Arut está
arquivado desde 2017. Pra produção em 2026 use o fork da arut/nginx-rtmp-module
ainda funcional pra recursos clássicos, ou o sergey-dryabzhinsky/nginx-rtmp-module
que continua recebendo patches.
Crie um diretório de trabalho e baixe o Nginx estável:
mkdir -p ~/build && cd ~/build
wget https://nginx.org/download/nginx-1.26.2.tar.gz
tar -xzvf nginx-1.26.2.tar.gzA versão 1.26.x é a linha estável atual. Evite a mainline (1.27.x) em
produção a menos que precise de feature específica.
Clone o módulo RTMP:
cd ~/build
git clone https://github.com/sergey-dryabzhinsky/nginx-rtmp-module.gitEsse fork mantém compatibilidade com Nginx moderno e tem bugfixes que o original arquivado não recebeu.
Compilar e instalar o Nginx com módulo RTMP
A compilação leva 4-5 minutos em vCPU moderna. O configure aceita
dezenas de flags — abaixo mantemos o conjunto mínimo necessário pra um
servidor de streaming HTTPS funcional.
Configure o build apontando pro 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-moduleSe algum --with-* reclamar de biblioteca faltando, instale o -dev
correspondente via apt e re-rode o configure.
Compile e instale:
make -j$(nproc)
sudo make install-j$(nproc) paraleliza a compilação no número de núcleos disponíveis.
Numa vCPU única leva ~7 minutos; em 4 vCPUs, ~2 minutos.
Crie o serviço systemd pra gerenciar o 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 nginxExecStartPre valida a configuração antes de subir — evita serviço quebrado
após edit.
Configurar a aplicação RTMP live
Agora editamos o nginx.conf pra adicionar o bloco rtmp (fora do bloco
http, no nível raiz) e configurar uma aplicação live que aceita
publicação do OBS e gera fragmentos HLS.
Crie o diretório onde os fragmentos HLS serão escritos:
sudo mkdir -p /var/www/hls
sudo chown -R nobody:nogroup /var/www/hlsO Nginx, sem usuário customizado configurado, roda como nobody. Ajuste se
você definir user www-data no nginx.conf.
Substitua /etc/nginx/nginx.conf por uma configuração que combina 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;
}
}
}Substitua 198.51.100.0/24 pelo seu IP/rede de origem real. O bloco
allow publish é a primeira camada de defesa — sem ele, qualquer pessoa
com sua URL RTMP publica.
Erro clássico: colocar o bloco rtmp dentro de http {}. Ele é
top-level, paralelo ao http. Se você colar dentro, o Nginx vai falhar no
nginx -t com mensagem confusa sobre unknown directive.
Valide a sintaxe e suba o serviço:
sudo nginx -t
sudo systemctl start nginxnginx -t deve responder configuration file ... test is successful.
Qualquer erro aqui aponta a linha exata — corrija antes de continuar.
Liberar portas no firewall
Sem regras de firewall, a porta 1935 fica exposta pro mundo inteiro. UFW no Ubuntu controla isso de forma simples.
Habilite UFW e libere as portas necessárias:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 1935/tcp
sudo ufw enableConfirme com sudo ufw status numbered que as 4 regras aparecem antes de
desconectar do SSH.
ufw enable aplica o default de negar todo tráfego entrante imediatamente.
Se você esquecer de liberar o OpenSSH primeiro, perde a sessão e precisa
acessar pelo console serial do provedor pra desbloquear.
Publicar do OBS
Com o servidor pronto, configure o OBS pra ingestão.
Abra OBS → Configurações → Transmissão e preencha:
Personalizado rtmp://SEU-IP/live minha-stream-001 A “chave” é o nome (name) que aparece nos fragmentos HLS e nos logs do
Nginx-RTMP. Pode ser qualquer string ASCII sem espaços.
Clique em “Iniciar transmissão” no OBS. O OBS conecta no rtmp://SEU-IP/live
e começa a publicar a stream com o nome minha-stream-001.
Verificar a transmissão
Confira que os fragmentos HLS estão sendo gerados e tocam num player web.
Liste os arquivos em /var/www/hls:
ls -la /var/www/hls/Output esperado, ~3 segundos após começar a publicar:
minha-stream-001-0.ts
minha-stream-001-1.ts
minha-stream-001.m3u8Os .ts são fragmentos MPEG-TS de ~3 segundos cada. O .m3u8 é o
manifesto que aponta pros fragmentos atuais.
Teste no navegador via https://hls-js.netlify.app/demo/ ou qualquer player
que aceite HLS. Cole http://SEU-IP/hls/minha-stream-001.m3u8 e dê play.
Pra um teste rápido sem player web, use ffplay:
ffplay http://SEU-IP/hls/minha-stream-001.m3u8A latência esperada do OBS ao player é de 9-15 segundos com a configuração
padrão acima (hls_fragment 3s × buffer de 3 fragmentos no player).
Resolução de problemas
OBS conecta mas nada aparece no /var/www/hls
Verifique permissão do diretório:
sudo -u nobody touch /var/www/hls/teste && rm /var/www/hls/teste
Se reclamar “Permission denied”, reaplique chown -R nobody:nogroup /var/www/hls.
Erro “Failed to connect to server” no OBS
Cheque se a porta 1935 está aberta de fora:
nc -zv SEU-IP 1935
Se falhar, confira UFW (sudo ufw status) e o firewall do provedor da VPS
— alguns têm firewall externo separado.
Player carrega o m3u8 mas dá “no supported source”
Falta CORS ou MIME type errado. Confirme que o response do .m3u8 traz
Content-Type: application/vnd.apple.mpegurl e Access-Control-Allow-Origin: *:
curl -I http://SEU-IP/hls/minha-stream-001.m3u8
Próximos passos
Você tem agora um servidor RTMP funcional convertendo pra HLS. Daqui dá pra aprofundar em várias direções:
- Adicionar HTTPS com Let’s Encrypt no servidor HTTP (Certbot funciona normal — o RTMP em si não recebe TLS direto, mas o HLS sim).
- Implementar autenticação via
on_publishapontando pra um endpoint seu que valida tokens contra banco. - Habilitar gravação automática com
record all+record_path /var/www/recordingspra arquivar tudo em FLV. - Transcodificar pra múltiplos bitrates com
exec ffmpegdentro do bloco application — necessário pra ABR (adaptive bitrate) decente. - Trocar HLS por LL-HLS (
hls_fragment 1s) ou WebRTC se precisar de latência sub-segundo.
Se você está colocando isso em produção, uma VPS Hostini de streaming já vem com banda dedicada e portas RTMP/1935 liberadas por padrão — sem precisar abrir ticket pra firewall externo.
Perguntas frequentes
Por que preciso compilar o Nginx do zero em vez de usar o pacote do apt?
O módulo nginx-rtmp não é dinâmico no Nginx estável do Ubuntu — ele precisa ser linkado em tempo de compilação via `--add-module`. O pacote `libnginx-mod-rtmp` existe em alguns mirrors, mas costuma ficar travado em versões antigas do módulo (1.2.1 de 2017). Compilar leva 4-5 minutos e te dá controle total da versão.
Qual a latência real de uma stream RTMP convertida pra HLS?
Entre 8 e 30 segundos com configuração padrão, dependendo do `hls_fragment` (default 5s) e do `hls_playlist_length`. Pra reduzir abaixo de 5s, considere LL-HLS com `hls_fragment 1s` e player compatível (hls.js 1.x), ou troque pra WebRTC se precisar de sub-segundo.
Posso transmitir do OBS direto pra HLS sem RTMP?
Não nativamente. O OBS exporta em RTMP, SRT ou WHIP (WebRTC). HLS é formato de distribuição pra player, não de ingestão. O fluxo padrão é OBS → RTMP → Nginx-RTMP gera HLS → player consome HLS via HTTP.
Como restrinjo quem pode publicar na stream sem expor a chave?
Use `on_publish` apontando pra um endpoint HTTP seu que valida o `name` (stream key) contra banco de dados. Nginx-RTMP só permite a publicação se o endpoint retornar 2xx. Combine com firewall fechando a porta 1935 só pra IPs autorizados se a base de transmissores for fixa.
Quantas streams simultâneas um servidor médio aguenta?
Depende de CPU e banda. Numa VPS de 4 vCPU + 8 GB sem transcodificação (apenas repassando RTMP e gerando HLS por fragmento de cópia direta), você atende 50-100 publishers concorrentes. Com transcodificação H.264 via ffmpeg pra múltiplos bitrates, cai pra 4-8 streams por CPU.
Os fragmentos HLS estão sendo gerados mas o player não consegue tocar — o que verificar?
Três causas comuns: 1) CORS — adicione `add_header 'Access-Control-Allow-Origin' '*'` no location HLS; 2) MIME type — confirme que `.m3u8` é `application/vnd.apple.mpegurl` e `.ts` é `video/mp2t`; 3) permissão do diretório — `/var/www/hls` precisa ser gravável pelo usuário do Nginx (geralmente www-data).