Skip to main content

NGINX

NGINX MPEG-TS Module for Live HLS and DASH Streaming

by ,


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

The NGINX MPEG-TS module (nginx-module-ts) transforms NGINX into a powerful live streaming origin server. It receives MPEG-TS streams over HTTP and automatically generates HLS (HTTP Live Streaming) and MPEG-DASH manifests and segments for playback on any device.

This NGINX module is ideal for scenarios where you need a lightweight, high-performance solution to ingest live video from encoders like FFmpeg, OBS Studio, or hardware encoders, and deliver adaptive bitrate streams to viewers.

Why Use the NGINX MPEG-TS Module?

Traditional streaming servers like Wowza or Adobe Media Server are often overkill for simple live streaming setups. The NGINX MPEG-TS module provides:

  • Lightweight operation: Runs as an NGINX module with minimal resource overhead
  • HTTP-based ingest: Receives streams via standard HTTP POST, simplifying firewall configuration
  • Dual output formats: Generates both HLS and MPEG-DASH from a single ingest point
  • Adaptive bitrate support: Automatically creates master playlists for multi-bitrate streams
  • Automatic cleanup: Manages segment files automatically, removing old segments as new ones arrive

If you need to add authentication to your streams, NGINX makes that straightforward with additional modules.

How the NGINX MPEG-TS Module Works

The module operates in three phases:

  1. Ingest: An encoder sends an MPEG-TS stream to NGINX via HTTP POST
  2. Processing: The module parses the transport stream, extracts audio and video elementary streams
  3. Output: Segments and playlists are written to disk in HLS (.m3u8 + .ts) or DASH (.mpd + .mp4) format

The stream name is derived from the last component of the request URI. For example, posting to /publish/livestream creates output in a livestream subdirectory.

Installation

RHEL, CentOS, AlmaLinux, Rocky Linux

Install the module from the GetPageSpeed repository:

sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-ts

Then load the module in your nginx.conf:

load_module modules/ngx_http_ts_module.so;

Debian and Ubuntu

First, set up the GetPageSpeed APT repository, then install:

sudo apt-get update
sudo apt-get install nginx-module-ts

On Debian/Ubuntu, the package handles module loading automatically. No load_module directive is needed.

For detailed package information, visit the RPM module page or APT module page.

Configuration Directives

The NGINX MPEG-TS module provides three directives for configuring live streaming.

ts

Syntax: ts;
Context: location

Enables the MPEG-TS handler for the location. This directive is required for any streaming configuration. It tells NGINX to accept incoming MPEG-TS data and process it according to the ts_hls and ts_dash directives.

location /publish/ {
    ts;
    # Additional ts_hls or ts_dash configuration here
}

ts_hls

Syntax: ts_hls path=PATH [segment=MIN[:MAX]] [segments=NUMBER] [max_size=SIZE] [noclean]
Context: location

Enables HLS (HTTP Live Streaming) output generation.

Parameter Default Description
path required Directory where HLS files are written
segment 5s:10s Minimum and maximum segment duration
segments 6 Number of segments to keep in the playlist
max_size 16m Maximum segment size before forced split
noclean disabled Disable automatic deletion of old segments

The module creates a subdirectory for each stream under the specified path. For example, with path=/var/media/hls, a stream named live creates files in /var/media/hls/live/.

Example HLS configuration:

location /publish/ {
    ts;
    ts_hls path=/var/media/hls segment=10s segments=5;
    client_max_body_size 0;
}

ts_dash

Syntax: ts_dash path=PATH [segment=MIN[:MAX]] [segments=NUMBER] [max_size=SIZE] [noclean]
Context: location

Enables MPEG-DASH output generation. Parameters are identical to ts_hls.

location /publish/ {
    ts;
    ts_dash path=/var/media/dash segment=6s segments=4;
    client_max_body_size 0;
}

Practical Configuration Examples

Basic Live Streaming Setup

This configuration creates a complete live streaming server with both HLS and DASH output:

server {
    listen 8080;

    # Ingest endpoint - receives MPEG-TS from encoders
    location /publish/ {
        ts;
        ts_hls path=/var/media/hls segment=10s;
        ts_dash path=/var/media/dash segment=10s;

        # Allow unlimited upload size for continuous streaming
        client_max_body_size 0;
    }

    # Playback endpoint - serves HLS/DASH to viewers
    location /play/ {
        types {
            application/x-mpegURL m3u8;
            application/dash+xml mpd;
            video/MP2T ts;
            video/mp4 mp4;
        }
        alias /var/media/;

        # Disable caching for live content
        add_header Cache-Control "no-cache";

        # Enable CORS for web players
        add_header Access-Control-Allow-Origin "*";
    }
}

After creating this configuration:

mkdir -p /var/media/hls /var/media/dash
chown nginx:nginx /var/media /var/media/hls /var/media/dash
nginx -t && systemctl reload nginx

For more advanced header handling, see our guide on the pitfalls of add_header.

Streaming with FFmpeg

Send a live stream to the NGINX MPEG-TS module using FFmpeg:

ffmpeg -f lavfi -i "testsrc2=duration=60:size=1280x720:rate=30" \
       -f lavfi -i "sine=frequency=440:duration=60" \
       -c:v libx264 -preset fast -tune zerolatency -g 30 \
       -c:a aac -b:a 128k \
       -f mpegts http://your-server:8080/publish/livestream

The stream becomes available at:
– HLS: http://your-server:8080/play/hls/livestream/index.m3u8`
- DASH:
http://your-server:8080/play/dash/livestream/index.mpd`

Adaptive Bitrate Streaming (ABR)

The NGINX MPEG-TS module automatically creates HLS master playlists when receiving multi-program MPEG-TS streams. Use FFmpeg’s -program option to create multiple quality variants:

ffmpeg -f lavfi -i "testsrc2=duration=120:size=1280x720:rate=30" \
       -f lavfi -i "sine=frequency=440:duration=120" \
       -map 0:0 -map 1:0 -map 0:0 -map 1:0 \
       -c:v:0 libx264 -b:v:0 2500k -s:v:0 1280x720 -preset fast -g 30 \
       -c:a:0 aac -b:a:0 128k \
       -c:v:1 libx264 -b:v:1 800k -s:v:1 640x360 -preset fast -g 30 \
       -c:a:1 aac -b:a:1 64k \
       -program "st=0:st=1" -program "st=2:st=3" \
       -f mpegts http://your-server:8080/publish/abr

This creates:
index.m3u8 – Master playlist with bandwidth information
1.m3u8 – High quality variant (720p @ 2500kbps)
2.m3u8 – Low quality variant (360p @ 800kbps)

The master playlist format:

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=2628000
1.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=864000
2.m3u8

Video players that support adaptive bitrate streaming will automatically switch between quality levels based on network conditions.

Recording Mode with noclean

For recording scenarios where you want to preserve all segments after the stream ends:

location /record/ {
    ts;
    ts_hls path=/var/media/recordings segment=10s noclean;
    client_max_body_size 0;
}

Without noclean, the module automatically removes segments that are no longer referenced in the playlist.

Testing and Verification

Verify Module Installation

Check that the module file exists:

ls -la /usr/lib64/nginx/modules/ngx_http_ts_module.so

Verify the module is loaded:

nginx -T 2>&1 | grep ts_module

Test Configuration Syntax

Always test your configuration before reloading:

nginx -t

Verify Stream Processing

Start a test stream and check the output:

# Start a 30-second test stream
ffmpeg -f lavfi -i "testsrc2=duration=30:size=640x360:rate=25" \
       -c:v libx264 -preset ultrafast -g 25 \
       -f mpegts http://localhost:8080/publish/test

# Check generated files
ls -la /var/media/hls/test/

# View the playlist
cat /var/media/hls/test/index.m3u8

Expected playlist output:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:10

#EXTINF:5.000,
0.ts
#EXTINF:5.000,
1.ts
#EXTINF:5.000,
2.ts

Test HTTP Playback

Verify content is accessible via HTTP:

curl -I http://localhost:8080/play/hls/test/index.m3u8

Expected response:

HTTP/1.1 200 OK
Content-Type: application/x-mpegURL

Performance Considerations

Segment Duration

Shorter segments (2-4 seconds) reduce latency but increase HTTP requests. Longer segments (8-12 seconds) are more efficient but increase start-up time. A segment duration of 5-10 seconds works well for most live streaming scenarios.

Keyframe Interval

The encoder’s keyframe interval (GOP size) should match the minimum segment duration. If keyframes are infrequent, segments may extend to the maximum duration, causing inconsistent playback.

For a 10-second segment:

ffmpeg ... -g 300 ...  # Keyframe every 300 frames (10s at 30fps)

Disk I/O

The module continuously writes segments to disk. For high-bitrate streams, use:

  • Fast storage (SSD or NVMe)
  • Dedicated partition for media files
  • tmpfs for extremely low latency (if RAM permits)

Example tmpfs configuration in /etc/fstab:

tmpfs /var/media/live tmpfs size=2G,mode=0755 0 0

Worker Processes

Each stream consumes a worker connection. Ensure worker_connections is sufficient for your expected concurrent streams plus viewers. For optimal NGINX performance, proper buffer tuning also matters.

Troubleshooting

Stream Not Starting

Symptom: FFmpeg reports “Connection refused”

Solutions:
1. Verify NGINX is running: systemctl status nginx
2. Check the module is loaded: nginx -T | grep ts_module
3. Verify the port is listening: ss -tlnp | grep nginx

Empty Output Directory

Symptom: No files appear in the HLS/DASH directory

Solutions:
1. Check directory permissions: ls -la /var/media/
2. Ensure directory ownership: chown nginx:nginx /var/media/hls
3. Verify client_max_body_size is set: client_max_body_size 0;

Playlist Shows Wrong Duration

Symptom: #EXT-X-TARGETDURATION doesn’t match configuration

Cause: The value is calculated based on maximum segment duration, not minimum.

With segment=5s:10s, target duration will be 10 seconds.

Segments Disappearing

Symptom: Segment files are deleted while streaming

Cause: This is normal behavior. The module removes segments older than segments × max_segment_duration.

Solution: Use noclean for recording scenarios.

Comparison with NGINX RTMP Module

Feature MPEG-TS Module RTMP Module
Ingest Protocol HTTP RTMP
Firewall Friendly Yes (HTTP/HTTPS) Requires port 1935
Setup Complexity Simple Moderate
VOD Support No Yes
Recording Via noclean Built-in
Transcoding External only External only

Choose the NGINX MPEG-TS module when you need HTTP-based ingest, particularly for cloud environments where only HTTP/HTTPS ports are available.

Conclusion

The NGINX MPEG-TS module provides a simple yet powerful solution for live streaming directly from NGINX. With HTTP-based ingest, automatic HLS and MPEG-DASH generation, and adaptive bitrate support, it handles common streaming scenarios without requiring additional streaming software.

Key takeaways:
– Use ts directive to enable the handler
– Configure ts_hls and/or ts_dash for output format
– Set client_max_body_size 0 for continuous streaming
– Use -program in FFmpeg for adaptive bitrate streams
– Add noclean when recording is needed

For the latest updates and to report issues, visit the nginx-ts-module GitHub repository.

D

Danila Vershinin

Founder & Lead Engineer

NGINX configuration and optimizationLinux system administrationWeb performance engineering

10+ years NGINX experience • Maintainer of GetPageSpeed RPM repository • Contributor to open-source NGINX modules

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.