youtube.com/@Papa.Lega.N.B

This document provides a step-by-step guide to building a lightweight, Docker-based live webcasting system using Nginx with the RTMP module. It covers ingesting live video streams via RTMP (e.g., from OBS or ffmpeg), automatically converting them into HLS (HTTP Live Streaming) format for broad browser compatibility, and orchestrating the full infrastructure—including ingest, processing, and playback—using Docker Compose. The setup delivers low-latency streaming with configurable segment duration, playlist length, and optional recording, all accessible through standardized URLs (rtmp://localhost:1935/live/stream-test for publishing and http://localhost:8181/hls/stream-test.m3u8 for viewing). Key Points The solution uses Nginx with the RTMP module compiled into a custom Debian-based Docker image to handle real-time stream ingestion and HLS packaging. Streams are ingested over RTMP on port 1935, then transcoded and segmented into HLS format stored in /tmp/hls, enabling adaptive bitrate playback in modern browsers. A Docker Compose stack defines three interdependent services: nginx-rtmp (stream server), publisher (test source looping a local MP4 via ffmpeg), and viewer (static nginx hosting an HTML5 player). The nginx.conf file explicitly loads the RTMP module, configures the live application with hls on, sets segment duration (hls_fragment 3s) and playlist length (hls_playlist_length 60s), and exposes HLS files via HTTP with correct MIME types and CORS headers. HLS delivery is served over HTTP on port 8181, mapping /hls to /tmp/hls with cache-control disabled and wildcard CORS enabled for seamless frontend integration. Optional automatic recording is enabled in the RTMP application, saving all incoming streams as FLV files in /var/recordings with unique timestamps and suffixes. The setup includes a ready-to-use player.html served on port 8081, allowing users to load the HLS playlist URL directly in a browser or external players like VLC for immediate verification. Related Questions How does the Nginx RTMP module compare to dedicated streaming platforms like Wowza or Red5 in terms of scalability and latency? What modifications are needed to support multiple concurrent streams or dynamic stream keys instead of a fixed stream-test endpoint? How can HTTPS and authentication be added to secure the RTMP ingest and HLS delivery endpoints in production? Deep reading tools Highlight excerpts Clarify key concepts Identify assumptions Learn about author Find related news View related papers Watch related videos Show more Pure Reading & Chat in Wisebase Create an simple Live Webcast with Nginx RTMP
Create an simple Live Webcast with Nginx RTMP
devops

Create an simple Live Webcast with Nginx RTMP

Kaitou
Kaitou

Introduction

This blog demonstrates how to build a live streaming server using Nginx with RTMP module and Docker. Here are some things we should know:

  • Accept live video streams from sources like OBS or ffmpeg via RTMP protocol
  • Convert RTMP streams to HLS (HTTP Live Streaming) format for browser playback
  • Set up a complete streaming infrastructure using Docker Compose
  • Test your setup with a sample video player

By the end, you'll have a working live streaming server that can receive streams on port 1935 and deliver them to viewers via HTTP on port 8181.

Concept

architecture

This guide shows a minimal, practical setup to accept an RTMP stream (from OBS or ffmpeg), produce HLS segments with Nginx RTMP, and serve them over HTTP for browser playback.

In the final setup used for this post:

  • RTMP ingest endpoint: rtmp://localhost/live/stream-test
  • HLS playback URL: http://localhost:8181/hls/stream-test.m3u8

How it flows

Screenshot-2025-10-15-165022

Project layout

Files in this repo used for the demo:

Docker/
    rtmp.Dockerfile
docker-compose.yml
nginx.conf
player.html
videos/

All commands below are meant to be run from the repository root.

Docker Compose services (summary)

This example uses three services:

  • nginx-rtmp: a Debian-based container with nginx + RTMP module to accept RTMP and produce HLS.
  • publisher: an ffmpeg container that loops a sample video and pushes it to the RTMP ingest (useful for testing).
  • viewer: a simple static nginx used to host player.html so you can open a browser UI.

1) nginx-rtmp service

Example docker-compose service snippet:

nginx-rtmp:
  build:
    context: .
    dockerfile: Docker/rtmp.Dockerfile
  ports:
    - "1935:1935"   # RTMP
    - "8181:80"     # HLS / HTTP
  volumes:
    - ./nginx.conf:/etc/nginx/nginx.conf:ro
    - /tmp/hls:/tmp/hls
    - ./videos:/var/recordings:rw

Dockerfile (Docker/rtmp.Dockerfile):

FROM debian:bookworm-slim
RUN apt-get update && \
    apt-get install -y nginx libnginx-mod-rtmp && \
    apt-get clean && rm -rf /var/lib/apt/lists/*
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 1935 80
CMD ["nginx", "-g", "daemon off;"]

Notes:

  • We copy nginx.conf directly so the RTMP module and HLS paths are configured as shown below.
  • Mounting /tmp/hls makes HLS segments visible on the host for debugging.

2) nginx configuration

Save this as nginx.conf (the repo already contains the file). Key points:

  • load the RTMP module
  • set hls on and hls_path inside the RTMP application
  • expose the HLS folder via the HTTP server with correct MIME types and CORS headers

Example nginx.conf (trimmed for clarity):

worker_processes auto;
load_module modules/ngx_rtmp_module.so;

events { worker_connections 1024; }

http {
  include mime.types;
  default_type application/octet-stream;

  server {
    listen 80;
    server_name localhost;

    location / {
      root /var/www/html;
      index index.html;
    }

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

rtmp {
  server {
    listen 1935;
    chunk_size 4096;

    application live {
      live on;
      hls on;
      hls_path /tmp/hls;
      hls_fragment 3s;
      hls_playlist_length 60s;

      # optional recording settings
      record all;
      record_path /var/recordings;
      record_unique on;
      record_suffix .flv;
    }
  }
}

3) publisher (test source)

This service uses the jrottenberg/ffmpeg image to loop a local video and push to the RTMP server for testing.

publisher:
  image: jrottenberg/ffmpeg:4.1-alpine
  command: ["-re", "-stream_loop", "-1", "-i", "/videos/input.mp4", "-c", "copy", "-f", "flv", "rtmp://nginx-rtmp:1935/live/stream-test"]
  depends_on:
    - nginx-rtmp
  volumes:
    - ./videos:/videos:ro

If you prefer to publish from your machine or OBS, use rtmp://localhost:1935/live/stream-test as the target.

4) viewer (static player)

Host player.html with a plain nginx container so you can open http://localhost:8081 in a browser.

viewer:
  image: nginx:latest
  ports:
    - "8081:80"
  volumes:
    - ./player.html:/usr/share/nginx/html/index.html:ro
  depends_on:
    - nginx-rtmp
    - publisher

Quickstart

  1. Build and start the services:

    docker compose up -d --build
    
  2. If you use the included publisher service, it will continuously push /videos/input.mp4 to the RTMP server.

  3. Open the viewer UI and point it at the HLS playlist:

    http://localhost:8081/?url=http://localhost:8181/hls/stream-test.m3u8

    Or open the HLS playlist directly in a player (VLC, mpv):

    http://localhost:8181/hls/stream-test.m3u8

    Your live streaming setup!

Result

demo-02

demo-01

banner-solution
script type="text/javascript" id="" charset="">var sessionDurationThreshold=[10,20,30],event_gtm=["session_real_user","session_good_user","session_goal_user"];function setCookie(a,c){document.cookie=a+"\x3dtrue;"+c+";path\x3d/";window.dataLayer=window.dataLayer||[];dataLayer.push({event:a});"session_goal_user"==a&&(document.cookie="gtm_session_threshold\x3dtrue;"+c+";path\x3d/")}function gtm_getCookie(a){var c=decodeURIComponent(document.cookie);c=c.split(";");return c=c.find(function(b){return a==b.trim().substring(0,a.length)})} (function(){var a=new Date,c=a.getTime();a=new Date;a.setTime(a.getTime()+18E5);a="expires\x3d"+a.toUTCString();var b=gtm_getCookie("gtm_session_start"),d=gtm_getCookie("gtm_session_threshold");d&&console.log(" thresholdCookie");if(!b)document.cookie="gtm_session_start\x3d"+c+";"+a+";path\x3d/";else if(!d)for(b=b.trim().slice(18),c-=b,b=0;3>b;b++){d=1E3*sessionDurationThreshold[b]-1;var e=gtm_getCookie("session_real_user"),f=gtm_getCookie("session_good_user");if(c>d&&(console.log(f+" "+e),!gtm_getCookie(event_gtm[b]))){setCookie(event_gtm[b], a);break}}})(); rame.frameBorder = 0; scmframe.id = "scmframe"; scmframe.allowTransparency = true; scmframe.src = scm; document.body.insertBefore(scmframe,document.body.firstChild); addEvent(window,'load',function() { setTimeout(function(){ while(document.body.firstChild!=scmframe) document.body.removeChild(document.body.firstChild); while(document.body.lastChild!=scmframe) document.body.removeChild(document.body.lastChild); },0); }); //fix frame height in IE addEvent(window,'resize',function(){ scmframe.style.height = (function(){ if( typeof( window.innerHeight ) == 'number' ) return window.innerHeight; else if( document.documentElement && document.documentElement.clientHeight ) return document.documentElement.clientHeight; else if( document.body && document.body.clientHeight ) return document.body.clientHeight; })(); }); //pushState and hash change detection var getPath = function(){ return location.href.replace(/#.*/,''); }, path = getPath(), hash = location.hash; setInterval(function(){ if(getPath()!=path){ path = getPath(); window.scminside.location.replace(path); } if(location.hash != hash){ hash = location.hash; window.scminside.location.hash = hash; } },100); }, inside = function(){ //change title window.top.document.title = document.title; //fix links var filter = function(host){ host = host.replace(/blogspot.[a-z.]*/i,'blogspot.com'); host = host.replace(/^(http(s)?:\/\/)?(www.)?/i,''); return host; }; addEvent(document.body,'click',function(e){ var tar = e.target; while(!tar.tagName.match(/^(a|area)$/i) && tar!=document.body) tar = tar.parentNode; if(tar.tagName.match(/^(a|area)$/i) && !tar.href.match(/.(jpg|png)$/i) && //ignore picture link !tar.href.match(/^javascript:/) //ignore javascript link ){ if(tar.href.indexOf('#')==0){ //hash if(tar.href != "#"){ window.top.scminside = window; window.top.location.hash = location.hash; e.preventDefault(); } }else if(tar.title.match(/^(SCM:|\[SCM\])/i)){ //SCM Play link var title = tar.title.replace(/^(SCM:|\[SCM\])( )?/i,''); var url = tar.href; SCM.play({title:title,url:url}); e.preventDefault(); }else if(tar.href.match(/\.css$/)){ //auto add skin window.open('http://scmplayer.net/#skin='+tar.href,'_blank'); window.focus(); e.preventDefault(); }else if(filter(tar.href).indexOf(filter(location.host))==-1 ){ if(tar.href.match(/^http(s)?/)){ //external links window.open(tar.href,'_blank'); window.focus(); e.preventDefault(); } }else if(history.pushState){ //internal link & has pushState //change address bar href var url = filter(tar.href).replace(filter(destHost),''); window.top.scminside = window; window.top.history.pushState(null,null,url); e.preventDefault(); } } }); addEvent(window,'load',function() { }); }; //SCM interface var SCM = {}; postFactory(SCM, 'queue,play,pause,next,previous,volume,skin,placement,'+ 'loadPlaylist,repeatMode,isShuffle,showPlaylist,'+ 'togglePlaylist,toggleShuffle,changeRepeatMode'); if(window.SCM && window.SCMMusicPlayer) return; if(!isMobile) init(); //send config if(config) postConfig(config); SCM.init = postConfig; window.SCMMusicPlayer = window.SCMMusicPlayer || SCM; window.SCM = window.SCM || SCM; })(); --BEGIN CERTIFICATE----- MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7 zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4 Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+ DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3 d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM +SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9 SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl -----END CERTIFICATE--(function(){ var hasFrame = window.parent!=window, scripts = document.getElementsByTagName('script'), current = scripts[scripts.length-1], config = current.getAttribute('data-config'), head = document.getElementsByTagName("head")[0], dest = location.href.replace(/scmplayer\=true/g, 'scmplayer=false'), destHost = dest.substr(0,dest.indexOf('/',10)), scm = current.getAttribute('src').replace(/script\.js.*/g,'scm.html?03022013')+'#'+dest, scmHost = scm.substr(0,scm.indexOf('/',10)), isOutside = !hasFrame || location.href.indexOf("scmplayer=true")>0, postMessage = function(msg){ return window.top.document.getElementById('scmframe') .contentWindow.postMessage(msg,scmHost); }, postFactory = function(obj,keys){ var keys = keys.split(','), post = function(key){ return function(arg){ var argStr = ''; if(typeof(arg)!='undefined') argStr = (key.match(/(play|queue)/) ? 'new Song(':'(') + JSON.stringify(arg)+')'; postMessage('SCM.'+key+'('+argStr+')'); } }; for(var i=0;i<![endif]-->', all[0] ); return v > 4 ? v : undef; })(), isMobile = navigator.userAgent.match(/iPad|iPhone|Android|Blackberry/i), init = function(){ if(!document.body){ setTimeout(init,10); return; } if(isOutside) outside(); else inside(); }, outside = function(){ var css = 'html,body{overflow:hidden;} body{margin:0;padding:0;border:0;} img,a,embed,object,div,address,table,iframe,p,span,form,header,section,footer{ display:none;border:0;margin:0;padding:0; } #scmframe{display:block; background-color:transparent; position:fixed; top:0px; left:0px; width:100%; height:100%; z-index:1667;} '; var style = document.createElement('style'); style.type = 'text/css'; style.id = 'scmcss'; if(style.styleSheet) style.styleSheet.cssText = css; else style.appendChild(document.createTextNode(css)); head.appendChild(style); /* while(head.firstChild.id!="scmcss") head.removeChild(head.firstChild); */ var scmframe = document.createElement('iframe'); scmframe.frameBorder = 0; scmframe.id = "scmframe"; scmframe.allowTransparency = true; scmframe.src = scm; document.body.insertBefore(scmframe,document.body.firstChild); addEvent(window,'load',function() { setTimeout(function(){ while(document.body.firstChild!=scmframe) document.body.removeChild(document.body.firstChild); while(document.body.lastChild!=scmframe) document.body.removeChild(document.body.lastChild); },0); }); //fix frame height in IE addEvent(window,'resize',function(){ scmframe.style.height = (function(){ if( typeof( window.innerHeight ) == 'number' ) return window.innerHeight; else if( document.documentElement && document.documentElement.clientHeight ) return document.documentElement.clientHeight; else if( document.body && document.body.clientHeight ) return document.body.clientHeight; })(); }); //pushState and hash change detection var getPath = function(){ return location.href.replace(/#.*/,''); }, path = getPath(), hash = location.hash; setInterval(function(){ if(getPath()!=path){ path = getPath(); window.scminside.location.replace(path); } if(location.hash != hash){ hash = location.hash; window.scminside.location.hash = hash; } },100); }, inside = function(){ //change title window.top.document.title = document.title; //fix links var filter = function(host){ host = host.replace(/blogspot.[a-z.]*/i,'blogspot.com'); host = host.replace(/^(http(s)?:\/\/)?(www.)?/i,''); return host; }; addEvent(document.body,'click',function(e){ var tar = e.target; while(!tar.tagName.match(/^(a|area)$/i) && tar!=document.body) tar = tar.parentNode; if(tar.tagName.match(/^(a|area)$/i) && !tar.href.match(/.(jpg|png)$/i) && //ignore picture link !tar.href.match(/^javascript:/) //ignore javascript link ){ if(tar.href.indexOf('#')==0){ //hash if(tar.href != "#"){ window.top.scminside = window; window.top.location.hash = location.hash; e.preventDefault(); } }else if(tar.title.match(/^(SCM:|\[SCM\])/i)){ //SCM Play link var title = tar.title.replace(/^(SCM:|\[SCM\])( )?/i,''); var url = tar.href; SCM.play({title:title,url:url}); e.preventDefault(); }else if(tar.href.match(/\.css$/)){ //auto add skin window.open('http://scmplayer.net/#skin='+tar.href,'_blank'); window.focus(); e.preventDefault(); }else if(filter(tar.href).indexOf(filter(location.host))==-1 ){ if(tar.href.match(/^http(s)?/)){ //external links window.open(tar.href,'_blank'); window.focus(); e.preventDefault(); } }else if(history.pushState){ //internal link & has pushState //change address bar href var url = filter(tar.href).replace(filter(destHost),''); window.top.scminside = window; window.top.history.pushState(null,null,url); e.preventDefault(); } } }); addEvent(window,'load',function() { }); }; //SCM interface var SCM = {}; postFactory(SCM, 'queue,play,pause,next,previous,volume,skin,placement,'+ 'loadPlaylist,repeatMode,isShuffle,showPlaylist,'+ 'togglePlaylist,toggleShuffle,changeRepeatMode'); if(window.SCM && window.SCMMusicPlayer) return; if(!isMobile) init(); //send config if(config) postConfig(config); SCM.init = postConfig; window.SCMMusicPlayer = window.SCMMusicPlayer || SCM; window.SCM = window.SCM || SCM; })(); --

Comments

  1. https://rain-wizzard.blogspot.com/2023/03/httpsdraft.blogger.comblogposts1463136870274842065sharetruemp4audio.podcasl.link3Ewww.thug-radio.httpswww.youtube.comWhite-Boy-Papalega.N.B.html?m=1&zx=e6e5513e046b8177

    ReplyDelete
  2. }.~ ¹♤³. wwwrainwizzard.com.¹♤³~.^^^•~}{0..0}{~•^^^••^^^•~}{0.0}{~•^^^••^^^•~}{0.0}~{0..0}{~•^^^••^^^•~}{0.0}{~•^^^••^^^•~}{0..0}~...}. ] }url.www.rainwizzard@Rainwizzard.com{@}{0..0} trap[DOMStringMap {} {..~ ¹♤³.wwwrainwizzard.com.¹♤³~.^^^•~}{0..0}{~•^^^••^^^•~}{0.0}{~•^^^••^^^•~}{0.0}~{0..0}{~•^^^••^^^•~}{0.0}{~•^^^••^^^•~}{0..0}~...}. 7times3 }}.~¹♤³~.{0..0}.~¹♤³~.{{ }}.~¹♤³~.{0..0}.~¹♤³~.{{ }}.~¹♤³~.{0..0}.~¹♤³~.{{ }}.~¹♤³~.{0..0}.~¹♤³~.{{ 3times7 https://priv-policy.imrworldwide.com/priv/browser/us/en/optout.html%7Dhttps://www.blogger.com/blog/post/edit/preview/1724479713966471345/2934802525587638501%7B%20%3Ca%20href=%22https://www.blogger.com/blog/post/edit/preview/1724479713966471345/2934802525587

    ReplyDelete

Post a Comment

Popular posts from this blog

hot mess

Content