Site icon GetPageSpeed

Kaltura NGINX Module for Live Streaming

NGINX Kaltura Live Common Module: Foundation for Distributed Live Streaming

You have an RTMP encoder pushing a live video feed to your NGINX server. Everything works for a single stream on a single box — but then the requirements grow. You need adaptive bitrate, DVR, low-latency HLS, and multi-server scaling. The standard NGINX RTMP module cannot do any of this, which is exactly the problem the Kaltura NGINX module for live streaming was built to solve.

The Kaltura Media Framework replaces the monolithic approach with a distributed, modular architecture where each stage of the live streaming pipeline — ingestion, segmentation, and packaging — runs as an independent NGINX module. The nginx-module-media-framework package bundles all 11 modules into a single install. It gives your NGINX server a complete live streaming pipeline with RTMP ingestion, HLS/LLHLS and DASH output, media encryption, closed caption extraction, and a JSON REST API for orchestration.

This article covers how to install and configure the Kaltura NGINX module package, how the modules work together, and how to manage live channels through the built-in API.

What Does the Kaltura Media Framework Do?

The Kaltura Media Framework is an open-source, distributed framework for live video streaming. Unlike monolithic streaming servers, it breaks the live pipeline into discrete NGINX modules that communicate using custom internal protocols.

Each module in the nginx-module-media-framework package handles one function:

Module .so File Function
Live Common ngx_http_api_module.so Shared API layer, JSON parser, protocol definitions
RTMP ngx_rtmp_module.so RTMP server for ingest
RTMP-KMP Bridge ngx_rtmp_kmp_module.so Converts RTMP input to KMP tracks
MPEG-TS ngx_stream_ts_module.so MPEG-TS demuxer for SRT/TCP ingest
TS-KMP Bridge ngx_ts_kmp_module.so Converts MPEG-TS input to KMP tracks
KMP In ngx_kmp_in_module.so Receives KMP tracks from bridges
KMP Out ngx_kmp_out_module.so Sends KMP tracks to external systems
Segmenter ngx_live_module.so Splits streams into segments, manages channels
Packager ngx_http_pckg_core_module.so Converts segments to HLS/LLHLS/DASH
CC Decoder ngx_stream_kmp_cc_module.so Extracts EIA-608/CTA-708 captions to WebVTT
RTMP Relay ngx_stream_kmp_rtmp_module.so Relays streams to YouTube/Twitch via RTMP

All 11 modules install together as a single RPM or DEB package. You load only the modules you need for your deployment topology.

How the Pipeline Works

Simple Setup: RTMP In, HLS/DASH Out

The most common topology accepts RTMP from an encoder and delivers HLS or DASH to viewers:

  1. Encoder pushes RTMP to ngx_rtmp_module (port 1935)
  2. The RTMP-KMP bridge converts the RTMP stream into individual KMP tracks (one per video/audio track)
  3. The segmenter (ngx_live_module) receives KMP tracks and splits them into segments
  4. The packager (ngx_http_pckg_core_module) converts segments to HLS, LLHLS, or DASH on the fly
  5. Viewers request the HLS/DASH manifest and segments over HTTP

Scaling with Distributed Deployment

Because each module is a separate NGINX component, you can deploy them on different servers. For example, run the segmenter on a high-memory machine and the packager on edge servers close to viewers. Each step is independently scalable — this is one of the key advantages of the Kaltura NGINX module architecture.

Additional Features

Installation

RHEL, CentOS, AlmaLinux, Rocky Linux

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

Then load the modules in your nginx.conf. Load order matters — dependencies must come first:

load_module modules/ngx_http_api_module.so;
load_module modules/ngx_rtmp_module.so;
load_module modules/ngx_stream_ts_module.so;
load_module modules/ngx_kmp_in_module.so;
load_module modules/ngx_kmp_out_module.so;
load_module modules/ngx_rtmp_kmp_module.so;
load_module modules/ngx_ts_kmp_module.so;
load_module modules/ngx_stream_kmp_cc_module.so;
load_module modules/ngx_stream_kmp_rtmp_module.so;
load_module modules/ngx_live_module.so;
load_module modules/ngx_http_pckg_core_module.so;

ngx_http_api_module.so must be loaded first — it is the shared foundation that every other Kaltura NGINX module depends on. Similarly, ngx_rtmp_module.so must come before ngx_rtmp_kmp_module.so which depends on it.

For detailed package information and version history, see the RPM module page.

Debian and Ubuntu

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

sudo apt-get update
sudo apt-get install nginx-module-media-framework

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

For package details, see the APT module page.

Minimal Configuration

Here is the smallest working configuration for the Kaltura NGINX module. It provides a segmenter, a management API, and an HLS/DASH packager:

load_module modules/ngx_http_api_module.so;
load_module modules/ngx_kmp_in_module.so;
load_module modules/ngx_live_module.so;
load_module modules/ngx_http_pckg_core_module.so;

worker_processes 1;
error_log /var/log/nginx/error.log info;

events {
    worker_connections 1024;
}

live {
    preset main {}
}

stream {
    server {
        listen 8003;
        live_kmp;
    }
}

http {
    default_type application/octet-stream;

    map $uri $channel_id {
        ~/ch/(?P<result>[^/]+) $result;
        default '';
    }

    server {
        listen 80;
        server_name _;

        client_body_buffer_size 64k;

        pckg_channel_id $channel_id;

        location /clear/ {
            pckg;
        }

        location /api/live/ {
            live_api write=on upsert=on;
        }

        location /ksmp/ {
            live_ksmp;
        }
    }
}

This gives you:

The live block defines presets — named configurations for the segmenter. Every channel you create must reference a preset. The main preset uses default settings. You can also add a low-latency preset with ll_segmenter for LLHLS delivery.

Important: Single Worker Requirement

All stateful modules in the Kaltura Media Framework must run with worker_processes 1. Module state is maintained per worker process and is not shared across workers. With multiple workers, API calls and media connections may land on different workers, causing channels to appear missing or media to be lost.

In containerized deployments, run multiple containers instead of multiple workers to scale horizontally.

Production Configuration

A production deployment adds RTMP ingestion, encryption endpoints, CORS headers for cross-origin playback, and internal API access control. The following configuration is adapted from the upstream reference:

load_module modules/ngx_http_api_module.so;
load_module modules/ngx_rtmp_module.so;
load_module modules/ngx_stream_ts_module.so;
load_module modules/ngx_kmp_in_module.so;
load_module modules/ngx_kmp_out_module.so;
load_module modules/ngx_rtmp_kmp_module.so;
load_module modules/ngx_ts_kmp_module.so;
load_module modules/ngx_stream_kmp_cc_module.so;
load_module modules/ngx_stream_kmp_rtmp_module.so;
load_module modules/ngx_live_module.so;
load_module modules/ngx_http_pckg_core_module.so;

worker_processes 1;
error_log /var/log/nginx/error.log info;

events {
    worker_connections 1024;
}

live {
    preset main {}

    preset ll {
        ll_segmenter;
    }
}

rtmp {
    server {
        listen 1935;

        application live {
            live on;
            deny play all;

            kmp_ctrl_connect_url http://127.0.0.1:8001/control/;
            kmp_ctrl_publish_url http://127.0.0.1:8001/control/;
            kmp_ctrl_unpublish_url http://127.0.0.1:8001/control/;
            kmp_ctrl_republish_url http://127.0.0.1:8001/control/;
        }
    }
}

stream {
    # Segmenter (KMP input)
    server {
        listen 8003;
        live_kmp;
    }

    # Closed caption decoder (KMP input)
    server {
        listen 8004;
        kmp_cc;
        kmp_cc_out_ctrl_publish_url http://127.0.0.1:8001/control/;
    }

    # RTMP output relay (KMP input)
    server {
        listen 8005;
        resolver 8.8.8.8;
        kmp_rtmp;
    }
}

http {
    default_type application/octet-stream;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 60;

    map $uri $channel_id {
        ~/ch/(?P<result>[^/]+) $result;
        default '';
    }

    map $uri $timeline_id {
        ~/tl/(?P<result>[^/]+) $result;
        default 'main';
    }

    # External server (packager + HLS/DASH delivery)
    server {
        listen 80;
        server_name _;

        gzip on;
        gzip_types application/vnd.apple.mpegurl video/f4m
                   application/dash+xml text/xml text/vtt;
        gzip_proxied any;

        add_header Access-Control-Allow-Headers
                   'Origin,Range,Accept-Encoding,Referer,Cache-Control';
        add_header Access-Control-Expose-Headers
                   'Server,Content-Length,Content-Range,Date,Age';
        add_header Access-Control-Allow-Methods 'GET,HEAD,OPTIONS';
        add_header Access-Control-Allow-Origin '*';

        pckg_uri /ksmp_proxy/;
        pckg_channel_id $channel_id;
        pckg_timeline_id $timeline_id;
        pckg_m3u8_low_latency on;

        location /clear/ {
            pckg;
        }

        location /aes128/ {
            pckg;
            pckg_enc_scheme aes-128;
        }

        location /cbcs/ {
            pckg;
            pckg_enc_scheme cbcs;
        }

        location /ksmp_proxy/ {
            internal;
            proxy_pass http://127.0.0.1:8001/ksmp/;
            subrequest_output_buffer_size 20m;
        }
    }

    # Internal server (management APIs)
    server {
        listen 8001;

        allow 127.0.0.1/32;
        deny all;

        client_body_buffer_size 64k;

        # Controller endpoint (replace with your application)
        location /control/ {
            # Your controller handles publish/unpublish callbacks
            # See the upstream reference controller:
            # https://github.com/kaltura/media-framework/blob/master/conf/controller.php
            fastcgi_param SCRIPT_FILENAME /opt/controller.php;
            fastcgi_param REQUEST_URI     $request_uri;
            fastcgi_param QUERY_STRING    $query_string;
            fastcgi_param REQUEST_METHOD  $request_method;
            fastcgi_param CONTENT_TYPE    $content_type;
            fastcgi_param CONTENT_LENGTH  $content_length;

            fastcgi_pass unix:/run/php/php-fpm.sock;
        }

        # Segmenter KSMP output
        location /ksmp/ {
            live_ksmp;
        }

        # Management APIs
        location /api/live/ {
            live_api write=on upsert=on;
        }

        location /api/kmp_out/ {
            kmp_out_api write=on;
        }

        location /api/kmp_cc/ {
            kmp_cc_api write=on;
        }

        location /api/kmp_rtmp/ {
            kmp_rtmp_api write=on;
        }

        location /api/rtmp_kmp/ {
            rtmp_kmp_api write=on;
        }

        location /api/ts_kmp/ {
            ts_kmp_api write=on;
        }
    }
}

What the Production Config Adds

Compared to the minimal setup, this configuration enables:

It also adds operational features:

The kmp_ctrl_* directives define callback URLs that notify your controller when encoders connect, publish, or disconnect. The controller creates channels, timelines, and variants in response. Kaltura provides a reference PHP controller as a starting point.

Managing Channels with the REST API

Every Kaltura NGINX module exposes a JSON REST API for management and monitoring. The live segmenter API is the most important one — it manages channels, timelines, variants, and tracks.

Discovering API Routes

Use ?list=1 on any API path to discover available routes:

curl -s http://127.0.0.1:8001/api/live/?list=1
["channels","multi"]

System Status

A GET request to the API root returns the system status including NGINX version, uptime, and all active channels:

curl -s http://127.0.0.1:8001/api/live/?pretty=1
{
    "version": "0.1",
    "nginx_version": "1.28.2",
    "compiler": "gcc",
    "built": "Mar 12 2026 00:00:00",
    "pid": 29949,
    "uptime": 15,
    "channels": {}
}

Creating a Channel

Channels are the top-level objects in the segmenter. Each channel contains timelines, variants, and tracks. Create one with a POST request:

curl -X POST http://127.0.0.1:8001/api/live/channels \
  -H "Content-Type: application/json" \
  -d '{"id": "demo", "preset": "main"}'

Both id and preset are mandatory. The preset must match a preset defined in your live {} block (for example, main or ll).

Inspecting a Channel

Retrieve the full state of a channel with a GET request:

curl -s http://127.0.0.1:8001/api/live/channels/demo?pretty=1

This returns the channel’s memory usage, timelines, variants, tracks, and segment state.

Multi-Request Batching

Setting up a live channel typically requires creating the channel, a timeline, and one or more variants. Batch these into a single HTTP request to /multi:

curl -X POST http://127.0.0.1:8001/api/live/multi \
  -H "Content-Type: application/json" \
  -d '[
    {"method":"POST","uri":"/channels","body":{"id":"ch1","preset":"main"}},
    {"method":"POST","uri":"/channels/ch1/timelines","body":{"id":"main","active":true}},
    {"method":"POST","uri":"/channels/ch1/variants","body":{"id":"sd"}}
  ]'

The response is a JSON array with the status code for each sub-request:

[{"code": 201}, {"code": 201}, {"code": 201}]

Deleting a Channel

curl -X DELETE http://127.0.0.1:8001/api/live/channels/demo

Returns HTTP 204 on success.

Other Module APIs

Every module has its own API endpoint for monitoring. Here are the endpoints and what they expose:

Endpoint Routes Purpose
/api/live/ channels, multi Segmenter — manage channels, timelines, variants
/api/kmp_out/ tracks, multi KMP output — monitor outbound connections
/api/rtmp_kmp/ sessions, multi RTMP-KMP bridge — monitor RTMP sessions
/api/ts_kmp/ sessions, multi TS-KMP bridge — monitor MPEG-TS sessions
/api/kmp_cc/ sessions, multi CC decoder — monitor caption sessions
/api/kmp_rtmp/ upstreams, multi RTMP relay — monitor outbound RTMP

All POST and PUT requests require Content-Type: application/json. The API returns 415 if the header is missing or mandatory parameters are absent.

Buffer Size for Large API Payloads

When API request bodies exceed NGINX’s default buffer size, the body gets written to a temporary file. The Kaltura API does not support reading from temporary files. If you encounter issues with large payloads, increase the buffer:

client_body_buffer_size 64k;

When to Use the Kaltura Media Framework

The Kaltura NGINX module excels in scenarios where the standard RTMP module falls short:

Feature NGINX RTMP Module Kaltura Media Framework
Simple RTMP-to-HLS Yes Yes
Distributed deployment No Yes
GPU transcoding No Yes
S3 persistence / DVR No Yes
Low-latency HLS (LLHLS) No Yes
DASH output No Yes
Closed captions (608/708) No Yes
Media encryption / DRM No Yes
Adaptive bitrate Limited Full
Multi-server scaling Manual Built-in
REST API for management No Yes

For single-server setups with basic RTMP-to-HLS needs, the standalone NGINX RTMP module is simpler. Choose the Kaltura NGINX module when you need distributed processing, GPU transcoding, persistence, or advanced features like LLHLS and DRM.

If your use case does not require the full Kaltura Media Framework, consider these standalone NGINX streaming modules:

Troubleshooting

Module Load Order Errors

“Undefined symbol” errors when loading modules mean a dependency is loaded after the module that needs it. The correct order is:

  1. ngx_http_api_module.so (required by all other modules)
  2. ngx_rtmp_module.so (required by ngx_rtmp_kmp_module.so)
  3. ngx_stream_ts_module.so (required by ngx_ts_kmp_module.so)
  4. All remaining modules in any order

Conflict with NGINX Plus

NGINX Plus includes its own proprietary ngx_http_api_module. Loading the Kaltura NGINX module alongside NGINX Plus causes a conflict because both register under the same internal name. The Kaltura Media Framework is designed for open-source NGINX only.

Channels Disappear or API Returns 404

If API calls return 404 for objects you just created, verify that worker_processes is set to 1. With multiple workers, your creation request and your subsequent GET may hit different workers that do not share state.

API Returns 415 Unsupported Media Type

This means the Content-Type: application/json header is missing, the JSON body is malformed, or a mandatory parameter is absent. For channel creation, both id and preset are required.

Controller Callback Errors

If RTMP streams connect but channels are not created automatically, check that your controller endpoint is reachable at the URL in kmp_ctrl_connect_url. The controller must return JSON responses that instruct the framework on channel setup. See the reference controller for the expected format.

Conclusion

The nginx-module-media-framework package brings Kaltura’s distributed live streaming architecture to your NGINX server. It provides 11 tightly integrated modules that handle everything from RTMP ingestion to encrypted HLS/DASH delivery, with a JSON REST API for full programmatic control.

For the complete source code, controller examples, and protocol documentation, visit the Kaltura Media Framework on GitHub.

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

Exit mobile version