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:
- Encoder pushes RTMP to
ngx_rtmp_module(port 1935) - The RTMP-KMP bridge converts the RTMP stream into individual KMP tracks (one per video/audio track)
- The segmenter (
ngx_live_module) receives KMP tracks and splits them into segments - The packager (
ngx_http_pckg_core_module) converts segments to HLS, LLHLS, or DASH on the fly - 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
- GPU transcoding — Insert an ffmpeg-based transcoder between ingestion and segmentation for adaptive bitrate with multiple resolutions
- S3 persistence — The segmenter can persist segments to S3-compatible storage for DVR, catch-up TV, and crash recovery
- Closed captions — The CC decoder module extracts embedded EIA-608/CTA-708 captions from video tracks and produces WebVTT subtitle tracks
- RTMP relay — Push streams to external platforms like YouTube Live or Twitch
- Media encryption — Serve content with AES-128 or CBCS encryption for DRM
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_moduledirective 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:
- A KMP listener on port 8003 that receives segmented media tracks
- A management API at
/api/live/for creating and managing channels - A KSMP endpoint at
/ksmp/for internal segment retrieval - An HLS/DASH packager at
/clear/for delivering media to viewers
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:
- RTMP ingestion on port 1935 with controller callbacks for publish/unpublish events
- Closed caption decoding on port 8004 for extracting embedded captions
- RTMP relay on port 8005 for pushing streams to external platforms
- Three packager endpoints —
/clear/for unencrypted,/aes128/for AES-128, and/cbcs/for CBCS encryption
It also adds operational features:
- CORS headers for cross-origin playback in web players
- Internal API server on port 8001, restricted to localhost via
allow/deny - Low-latency preset (
ll) alongside the standardmainpreset - Timeline support via the
$timeline_idmap variable
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.
Related Streaming Modules on GetPageSpeed
If your use case does not require the full Kaltura Media Framework, consider these standalone NGINX streaming modules:
- NGINX HTTP-FLV Module — Enhanced RTMP/HTTP-FLV live streaming with HTTP callbacks
- NGINX MPEG-TS Module — Receive MPEG-TS input and produce live HLS and DASH output
- NGINX VOD Module — On-the-fly repackaging of MP4 files to HLS, DASH, and other formats (also by Kaltura)
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:
ngx_http_api_module.so(required by all other modules)ngx_rtmp_module.so(required byngx_rtmp_kmp_module.so)ngx_stream_ts_module.so(required byngx_ts_kmp_module.so)- 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.

