The NGINX VOD module transforms your web server into a powerful video-on-demand platform. Developed by Kaltura, this module repackages MP4 files on-the-fly to HLS, DASH, HDS, and MSS formats. No pre-processing or transcoding is required. System administrators will find it eliminates the complexity of maintaining multiple video formats.
Traditional streaming solutions require pre-packaged content in every format. The NGINX VOD module takes a different approach. It reads a single MP4 file and generates manifests dynamically. This simplifies content management and enables instant video availability.
Key Features
This module provides enterprise-grade video streaming capabilities:
- Multi-Protocol Support: HLS (Apple), DASH (MPEG), HDS (Adobe), and MSS (Microsoft Smooth Streaming)
- Working Modes: Local files, remote HTTP, or mapped JSON configurations
- Adaptive Bitrate: Multi-bitrate streaming with automatic manifest generation
- DRM Integration: CENC encryption for DASH, PlayReady for MSS, FairPlay and AES-128 for HLS
- Live Simulation: Generate live streams from VOD content for testing
- Thumbnail Generation: Extract thumbnails at any timestamp (requires ffmpeg)
- Content Clipping: Serve portions of videos without re-encoding
Installing the NGINX VOD Module
RHEL, CentOS, AlmaLinux, Rocky Linux
Install from the GetPageSpeed repository:
sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-vod
Enable it by adding to /etc/nginx/nginx.conf:
load_module modules/ngx_http_vod_module.so;
Debian and Ubuntu
First, set up the GetPageSpeed APT repository, then install:
sudo apt-get update
sudo apt-get install nginx-module-vod
On Debian/Ubuntu, the package handles module loading automatically. No
load_moduledirective is needed.
Verify it loads correctly:
nginx -t
Basic Configuration for HLS Streaming
Minimal configuration is needed for HLS. Here is a complete working example:
# Define caches in http context
vod_metadata_cache metadata_cache 512m;
vod_response_cache response_cache 128m;
vod_performance_counters vod_pc;
server {
listen 80;
server_name streaming.example.com;
# HLS streaming location
location /hls/ {
alias /var/www/videos/;
vod hls;
vod_mode local;
vod_segment_duration 10000;
vod_align_segments_to_key_frames on;
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS";
expires 1d;
}
}
Place MP4 files in /var/www/videos/ and access them via URLs like:
- Master playlist: `http://streaming.example.com/hls/video.mp4/master.m3u8`
- Media playlist: `http://streaming.example.com/hls/video.mp4/index-v1-a1.m3u8`
DASH Streaming Configuration
MPEG-DASH provides better cross-platform compatibility for non-Apple devices:
location /dash/ {
alias /var/www/videos/;
vod dash;
vod_mode local;
vod_segment_duration 10000;
vod_align_segments_to_key_frames on;
# DASH-specific options
vod_dash_manifest_format segmenttimeline;
vod_dash_profiles "urn:mpeg:dash:profile:isoff-main:2011";
add_header Access-Control-Allow-Origin "*";
expires 1d;
}
Access DASH content via `http://streaming.example.com/dash/video.mp4/manifest.mpd`.
Advanced Cache Configuration
Proper caching dramatically improves performance. Multiple cache types are supported:
# Metadata cache - stores MP4 moov atom data
# Allocate generously as this cache has the highest impact
vod_metadata_cache metadata_cache 1g 30d;
# Response cache - stores manifest responses
vod_response_cache response_cache 128m 1d;
# Live response cache - for time-varying live responses
vod_live_response_cache live_cache 64m 5m;
# Mapping cache - for mapped mode only
vod_mapping_cache mapping_cache 16m 30m;
The format is cache_name size [expiration]. Size uses standard nginx notation (k/m/g).
Bootstrap Segments for Faster Playback
Bootstrap segments allow players to select the optimal bitrate faster:
location /hls/ {
alias /var/www/videos/;
vod hls;
vod_mode local;
# First 3 segments are shorter (2s, 2s, 4s) then switch to 10s
vod_segment_duration 10000;
vod_bootstrap_segment_durations 2000;
vod_bootstrap_segment_durations 2000;
vod_bootstrap_segment_durations 4000;
vod_align_segments_to_key_frames on;
}
This reduces initial buffering time. Efficiency is maintained for the rest of playback.
HLS Encryption with AES-128
Protect video content with AES-128 encryption:
location /hls-secure/ {
alias /var/www/videos/;
vod hls;
vod_mode local;
vod_segment_duration 10000;
# Enable AES-128 encryption
vod_hls_encryption_method aes-128;
vod_secret_key "your_secret_prefix_$vod_filepath";
add_header Access-Control-Allow-Origin "*";
}
The encryption key is generated automatically. It is served at the .../encryption.key path. For production, protect the key endpoint with authentication.
For SAMPLE-AES encryption (required for certain devices):
vod_hls_encryption_method sample-aes;
DRM Configuration for Enterprise Deployments
Industry-standard DRM systems are supported for DASH and HLS.
Widevine/PlayReady (DASH CENC)
location /drm-dash/ {
alias /var/www/videos/;
vod dash;
vod_mode local;
vod_drm_enabled on;
vod_drm_upstream_location /drm-server;
vod_drm_request_uri "/get-keys?file=$vod_filepath";
vod_drm_info_cache drm_cache 64m 1h;
}
location /drm-server {
internal;
proxy_pass http://your-drm-server:8080;
}
FairPlay (HLS)
location /fairplay/ {
alias /var/www/videos/;
vod hls;
vod_mode local;
vod_hls_encryption_method sample-aes;
vod_hls_encryption_key_uri "skd://license-server/key";
vod_hls_encryption_key_format "com.apple.streamingkeydelivery";
vod_hls_encryption_key_format_versions "1";
vod_drm_enabled on;
vod_drm_upstream_location /drm-server;
}
Mapped Mode for Dynamic Content
Mapped mode fetches content paths from an upstream server or local JSON:
location /vod/ {
vod hls;
vod_mode mapped;
vod_upstream_location /api;
vod_mapping_cache mapping_cache 16m 30m;
}
location /api {
internal;
proxy_pass http://content-api:8080/media/;
}
The upstream server returns JSON describing the video sources:
{
"sequences": [
{
"clips": [
{
"type": "source",
"path": "/storage/videos/video1_720p.mp4"
}
]
}
]
}
This enables adaptive bitrate sets, playlists, and complex content assembly.
URL Parameters for Content Manipulation
URL path parameters enable content manipulation without re-encoding.
Clipping
Extract a portion of a video:
/hls/video.mp4/clipFrom/30000/clipTo/90000/master.m3u8
This serves seconds 30-90 of the video.
Track Selection
Select specific audio or video tracks:
/hls/video.mp4/tracks/v1-a2/master.m3u8
This selects video track 1 and audio track 2.
Time Shifting
Apply timestamp offsets:
/hls/video.mp4/shift/v100/master.m3u8
This shifts video timestamps by 100ms.
Performance Monitoring with Prometheus
Detailed performance metrics are available. Enable the status endpoint:
location /vod_status {
vod_status;
allow 10.0.0.0/8;
deny all;
}
Access metrics in two formats:
β XML format: http://server/vod_status`http://server/vod_status?format=prom`
- Prometheus format:
Key metrics to monitor:
vod_cache_fetch_hit/vod_cache_fetch_missβ Cache hit ratiosvod_perf_counter_sum{action="media_parse"}β MP4 parsing timevod_perf_counter_sum{action="total"}β Total request processing time
Reset counters: `http://server/vod_status?reset=1`
SELinux Configuration on RHEL-Based Systems
When SELinux is enforcing, set the correct context for video files:
semanage fcontext -a -t httpd_sys_content_t '/var/www/videos(/.*)?'
restorecon -Rv /var/www/videos
Asynchronous I/O for High Performance
Enable asynchronous file operations for improved throughput:
http {
# Enable AIO
aio on;
# Use thread pool for file opening (nginx 1.7.11+)
thread_pool default threads=32 max_queue=65536;
server {
location /hls/ {
alias /var/www/videos/;
vod hls;
vod_mode local;
# Use thread pool for file opening
vod_open_file_thread_pool default;
# File handle caching
open_file_cache max=1000 inactive=5m;
open_file_cache_valid 2m;
open_file_cache_min_uses 1;
open_file_cache_errors on;
}
}
}
Monitor async operations via the status page. Look for async_open_file counters.
Remote Mode for Distributed Storage
Read MP4 files from HTTP backends like S3 or origin servers:
location /hls/ {
vod hls;
vod_mode remote;
vod_upstream_location /storage;
# Optimize remote reads
vod_initial_read_size 32k;
vod_max_metadata_size 256m;
}
location /storage {
internal;
proxy_pass http://s3.amazonaws.com/video-bucket;
proxy_set_header Host s3.amazonaws.com;
}
Multi-Bitrate Adaptive Streaming
Serve multiple quality levels with a single URL structure. Use the multi-URL format:
/hls/videos/movie_,480p,720p,1080p,.mp4.urlset/master.m3u8
This generates a master playlist containing:
β http://server/hls/videos/movie_480p.mp4/index.m3u8`http://server/hls/videos/movie_720p.mp4/index.m3u8`
-
β `http://server/hls/videos/movie_1080p.mp4/index.m3u8`
Players automatically switch between quality levels based on bandwidth.
Available Variables
Several variables are exposed for use in configurations:
| Variable | Description |
|---|---|
$vod_filepath |
Current file path |
$vod_suburi |
Current sub-URI in multi-URL requests |
$vod_set_id |
Media set identifier |
$vod_sequence_id |
Current sequence identifier |
$vod_segment_time |
Segment timestamp (milliseconds since epoch) |
$vod_segment_duration |
Segment duration in milliseconds |
$vod_status |
Internal error code for debugging |
Use these for logging, access control, or DRM integrations.
Troubleshooting Common Issues
403 Forbidden Errors
Check file permissions and SELinux context:
ls -laZ /var/www/videos/
namei -l /var/www/videos/video.mp4
βngx_file_readerβ Errors
Ensure the file exists. Check the error log for the exact path.
Empty or Invalid Manifests
Verify the MP4 file has valid metadata:
ffprobe /var/www/videos/video.mp4
The moov atom must be at the file start. Use qt-faststart or ffmpeg -movflags +faststart.
Memory Issues
Increase cache sizes:
vod_metadata_cache metadata_cache 2g;
vod_max_metadata_size 256m;
Related Resources
- NGINX MPEG-TS Module for Live Streaming β For live HLS and DASH streaming
- NGINX PTA Module β Time-limited token authentication for secure video URLs
- RPM Package Page β Package documentation
- Module GitHub Repository β Source code and issues
Conclusion
The NGINX VOD module provides a robust foundation for video-on-demand delivery. It dynamically repackages content. This eliminates storage overhead from pre-transcoding. All major streaming protocols are supported. Deploy sophisticated video delivery infrastructure using the configurations in this guide.
For high-traffic deployments, combine with CDN caching. Edge locations serve manifests and segments. The origin handles on-the-fly packaging for cache misses.

