Skip to content

NGINX Configuration Deep Dive

This document breaks down the nginx.conf file deployed in the broadcaststream-nginx Docker container. NGINX plays two vital roles in our stack: it acts as a web server to deliver the video segments (HLS) to users and as a reverse proxy to route internet traffic to our internal Node.js API and MediaMTX server.

Full Code Context

Here are the most critical parts of the configuration and exactly what each line does.

Global & HTTP Block

nginx
worker_processes auto;
events { worker_connections 1024; }
  • worker_processes auto: Tells NGINX to create one worker process per CPU core. If your server has 4 cores, NGINX uses 4 workers to handle traffic efficiently.
  • worker_connections 1024: Each of those workers can handle 1,024 simultaneous connections (e.g. 4000+ total simultaneous viewers).
nginx
sendfile on;
tcp_nopush on;
tcp_nodelay on;
proxy_buffering off;
  • sendfile on: Directly copies files from the hard drive (SSD) to the network card, bypassing the CPU. This is the secret to high-performance video delivery.
  • tcp_nopush / tcp_nodelay: Optimizes how TCP packets are sent over the network to reduce latency for the end user.
  • proxy_buffering off: CRITICAL. This tells NGINX not to hold onto data before sending it. In a low-latency live stream, we want video chunks pushed to the user the millisecond they are generated.

HLS Video Delivery (Port 8080)

The server block listens on port 8080. When a user requests an HLS file (.m3u8 playlist or .ts video chunk), this block handles it.

nginx
location /hls {
    add_header 'Access-Control-Allow-Origin'  '*' always;
  • Wide open CORS: * means ANY website can embed the video player and fetch chunks from this server without the browser blocking it.
nginx
location ~* \.m3u8$ {
    add_header 'Cache-Control' 'no-cache, no-store, must-revalidate' always;
}
  • The Playlist Rule: .m3u8 files are text files that constantly update as new video is recorded. We force the browser (no-cache) to NEVER store this file. If the browser cached this file, the stream would immediately freeze because the browser would never fetch the newest video segments.
nginx
location ~* \.ts$ {
    add_header 'Content-Type' 'video/mp2t' always;
    add_header 'Cache-Control' 'public, max-age=3600, immutable' always;
}
  • The Video Segment Rule: .ts files are physical chunks of recorded video (e.g., seconds 0 to 1 of your stream). Once a piece of video happens, it never changes.
  • immutable: We tell the browser it is safe to cache this chunk for 1 hour (3600). This saves your server massive amounts of bandwidth if a user rewinds the live stream.

Cloudflare Tunnel Proxies

Because we only expose one URL via Cloudflare (e.g., https://your-tunnel.trycloudflare.com), NGINX must catch specific URLs and secretly forward them internally to other Docker containers.

nginx
location /api/ {
    proxy_pass http://api:4000/;
}
  • Node API: If the frontend Javascript needs the list of active streams, it calls /api/streams. NGINX silently forwards this request to the api container on port 4000, grabs the JSON response, and sends it back to the user.
nginx
location /ingest/ {
    proxy_pass http://mediamtx:8889/;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}
  • WebRTC WHIP Ingest: When a broadcaster uses their webcam in the browser, WebRTC signaling hits /ingest/.
  • Upgrade: WebRTC requires WebSockets (persistent open connections) for signaling. The Upgrade headers tell NGINX to upgrade the standard HTTP request into a persistent real-time connection and route it to MediaMTX on port 8889.