RTMP

Nginx RTMP: How to Set Up a Live Streaming Server (Complete Guide)

14 min read
Modern server room with blue illumination representing nginx RTMP streaming server infrastructure
Reading Time: 10 minutes

If you’ve ever wanted to run your own live streaming server without paying for a cloud platform, the nginx rtmp module is probably the first project you read about. It turns the same web server that powers a third of the internet into a working RTMP ingest endpoint, an HLS packager, and a multi-platform relay — for the cost of a small VPS.

This guide walks through what the nginx rtmp module actually does, how the data flow works, every directive you need for a basic setup, an example that produces both RTMP playback and an HLS playlist, the trade-offs you’ll hit at scale, and when it makes more sense to send your stream to a managed API instead.

What Is Nginx RTMP?

Nginx rtmp (also written as nginx-rtmp-module) is an open-source nginx module that adds support for the RTMP protocol, HLS, and MPEG-DASH to the nginx web server. Once compiled in or loaded as a dynamic module, nginx accepts RTMP publishes on TCP port 1935, plays the streams back to RTMP viewers, and can package the same input as HLS or DASH segments served over HTTP.

The original project — arut/nginx-rtmp-module — was created by Roman Arutyunyan and is licensed BSD-2-Clause. It has 14,000+ stars on GitHub, runs on Linux, FreeBSD, macOS, and Windows, and is the most widely deployed open-source RTMP server in the developer community. Active development on the original repo slowed years ago, but several community forks (notably nginx-rtmp-module by sergey-dryabzhinsky) keep it patched against modern nginx releases.

Attribute Value
Project nginx-rtmp-module
License BSD-2-Clause
Default ingest port 1935 (TCP)
Ingest protocol RTMP, RTMPS (with stunnel)
Output protocols RTMP, HLS, MPEG-DASH, FLV
Codecs H.264 video, AAC audio (passthrough; transcoding via FFmpeg exec)
Recording FLV files via record directive
Authentication HTTP callback (on_publish, on_play)
Platforms Linux, FreeBSD, macOS, Windows (limited)

How Does Nginx RTMP Work?

Nginx rtmp adds an rtmp { } block at the top level of nginx.conf, parallel to the standard http { } block. When nginx starts with the module loaded, it opens a TCP listener on port 1935 and waits for RTMP clients.

Here’s the data flow for a typical OBS-to-browser stream:

  1. Publisher connects. OBS, FFmpeg, or another RTMP encoder opens a TCP connection to rtmp://your-server/live/streamkey. Nginx accepts it and matches the URL against an application block.
  2. Stream is received. Nginx parses the RTMP handshake, then begins accepting H.264 video and AAC audio chunks. The chunk_size 4096; directive controls how big each RTMP message can be.
  3. Stream is fanned out. With live on;, nginx broadcasts the incoming feed to every RTMP viewer that has subscribed to the same path. This is the classic publisher-to-many-subscribers pattern.
  4. Optional packaging. If hls on; is set, nginx writes .ts segments and an .m3u8 playlist to disk every few seconds. An HTTP server block then serves those files to browsers and mobile players. This is the same packaging step you read about in RTMP to HLS conversion.
  5. Optional recording or relay. With record all;, nginx writes a .flv file for each session. With push rtmp://otherserver/live/key;, it forwards the stream to another endpoint — useful for multi-destination broadcasts.

The module never re-encodes by default. It moves bytes from publisher to subscriber and writes segments. If you need transcoding (different bitrates for adaptive bitrate streaming, or codec changes), you wire up an exec directive that calls ffmpeg per stream.

Key Features of the Nginx RTMP Module

The feature set is small but covers the core workflows for self-hosted live streaming.

RTMP Ingest and Playback

The module accepts RTMP publishes and serves RTMP playback on the same port. This is the use case 90% of people install it for: an open ingest URL that OBS, vMix, FFmpeg, or any live streaming encoder can push to.

HLS and DASH Packaging

Setting hls on; and dash on; turns the same RTMP input into an HTTP-delivered stream that browsers and mobile devices can play. This is what makes nginx rtmp practical for web playback — without it, you’d be limited to Flash players, which no modern browser supports.

Multi-Worker Streaming

The rtmp_auto_push on; directive lets nginx run multiple worker processes and automatically relay streams between them, so you can use all your CPU cores instead of being capped at one.

Stream Relay (Push and Pull)

push sends an incoming stream to one or more downstream RTMP servers. pull does the reverse — nginx fetches a remote stream when a viewer connects. This is how you build a small CDN tier or restream to YouTube and Twitch from a single ingest point.

Recording

The record directive saves incoming streams to FLV files. You can capture every session, only on demand via control API, or by file size. This is the typical building block for live RTMP stream archive workflows.

HTTP Callbacks

on_publish, on_play, on_record_done, and similar hooks fire HTTP requests to your application when streams start, stop, or finish recording. You use them for authentication, billing, and to trigger downstream processing.

Statistics

The module exposes an XML stats endpoint that you can serve through any HTTP location and style with the included XSL stylesheet. It shows active publishers, subscribers, bandwidth, and per-stream metadata.

Nginx RTMP vs. Other Streaming Servers

Nginx rtmp is one of several open-source RTMP server projects. Here’s how it compares to the most common alternatives:

Server Protocols License Best For Trade-Off
nginx rtmp RTMP, HLS, DASH BSD-2 Simple self-hosted ingest + HLS No WebRTC, slow upstream maintenance
SRS (Simple Realtime Server) RTMP, WebRTC, HLS, HTTP-FLV, SRT, DASH, GB28181 MIT Higher concurrency, modern protocols Steeper learning curve
Ant Media Server WebRTC, RTMP, SRT, HLS, CMAF Apache 2 (Community) / Commercial Sub-second WebRTC + transcoding Heavier, JVM-based
MistServer RTMP, WebRTC, HLS, DASH, MP4 Custom Many output formats Less community
Wowza Streaming Engine RTMP, WebRTC, SRT, HLS, DASH Commercial Enterprise features, support Paid license

If you only need RTMP ingest and HLS output and you’re comfortable in nginx config, the nginx rtmp module is the smallest, most predictable option. If you need WebRTC for sub-second latency, SRS or Ant Media is the better starting point. Compare more options in our list of the best video streaming servers.

How to Install the Nginx RTMP Module

There are two paths: install a pre-packaged version, or build nginx from source with the module compiled in. The first is faster; the second gives you the latest module version and lets you patch it.

Option 1: Install via Apt (Ubuntu/Debian)

The libnginx-mod-rtmp package ships in the Ubuntu repos and works for most use cases.

sudo apt update
sudo apt install libnginx-mod-rtmp

This installs nginx (if it’s not already present) and the RTMP dynamic module. The module is auto-loaded via /etc/nginx/modules-enabled/50-mod-rtmp.conf.

Option 2: Build From Source

Use this when you need the latest module commit, want HLS encryption, or are running a non-Debian distribution.

sudo apt install -y build-essential libpcre3-dev libssl-dev zlib1g-dev git
cd /tmp
wget https://nginx.org/download/nginx-1.26.2.tar.gz
tar -xzf nginx-1.26.2.tar.gz
git clone https://github.com/arut/nginx-rtmp-module.git
cd nginx-1.26.2
./configure --with-http_ssl_module --add-module=/tmp/nginx-rtmp-module
make
sudo make install

Nginx will install to /usr/local/nginx. Symlink the binary into your path, then create a systemd unit so it starts on boot.

Open the Firewall

RTMP listens on TCP 1935. If you’re running UFW:

sudo ufw allow 1935/tcp
sudo ufw allow 80/tcp
sudo ufw allow 8080/tcp

Port 80 or 8080 is for HTTP delivery of HLS segments. Port 1935 is for the RTMP ingest itself.

Basic Nginx RTMP Configuration

The minimum working config for a private RTMP server is about 15 lines. Add this to /etc/nginx/nginx.conf (or /usr/local/nginx/conf/nginx.conf if you built from source):

load_module modules/ngx_rtmp_module.so;

events {
    worker_connections 1024;
}

rtmp {
    server {
        listen 1935;
        chunk_size 4096;

        application live {
            live on;
            record off;
            allow publish 127.0.0.1;
            allow publish 192.168.1.0/24;
            deny publish all;
            allow play all;
        }
    }
}

http {
    server {
        listen 80;
        location /stat {
            rtmp_stat all;
            rtmp_stat_stylesheet stat.xsl;
        }
    }
}

After saving, run sudo nginx -t to validate syntax, then sudo systemctl reload nginx. You can now publish to rtmp:///live/ from OBS or FFmpeg, and play back from VLC at the same URL.

A few directives worth understanding:

  • live on; — Enables broadcast mode. Without it, nginx accepts publishes but doesn’t relay to subscribers.
  • record off; — Disables disk recording. Set to record all; to capture every session.
  • allow publish / deny publish — IP-based access control for publishers. Always restrict to your own IPs in production.
  • chunk_size 4096; — Larger chunks reduce CPU overhead at the cost of slightly higher latency. 4096 is a safe default.

Adding HLS Output for Browser Playback

RTMP playback works for VLC and other RTMP-aware players, but not for browsers. To stream to a tag, add HLS packaging:

rtmp {
    server {
        listen 1935;
        chunk_size 4096;

        application live {
            live on;
            record off;

            hls on;
            hls_path /var/www/hls;
            hls_fragment 3;
            hls_playlist_length 60;
        }
    }
}

http {
    server {
        listen 8080;

        location /hls {
            types {
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }
            root /var/www;
            add_header Cache-Control no-cache;
            add_header Access-Control-Allow-Origin *;
        }
    }
}

Create the HLS directory and give nginx write access:

sudo mkdir -p /var/www/hls
sudo chown www-data:www-data /var/www/hls

When a publisher pushes to rtmp://server/live/test, nginx will write /var/www/hls/test.m3u8 plus a rolling set of .ts segments. A browser using hls.js can play it from http://server:8080/hls/test.m3u8. This is the same packaging logic explained in our deeper HLS streaming write-up.

The hls_fragment setting controls segment length, which directly affects latency. Three seconds is the sweet spot for stability; one second pushes toward low latency streaming but increases segment overhead.

Advanced Configuration: Recording, Relay, and Auth

Once the basic stream is working, three patterns come up almost immediately.

Recording Every Stream

application live {
    live on;
    record all;
    record_path /var/recordings;
    record_unique on;
    record_suffix -%Y-%m-%d-%H_%M_%S.flv;
}

record_unique on; adds a timestamp so concurrent sessions don’t overwrite each other. To convert the resulting FLV into MP4 after recording, use the on_record_done callback to trigger an FFmpeg job.

Restreaming to YouTube and Twitch

application live {
    live on;
    record off;
    push rtmp://a.rtmp.youtube.com/live2/YOUR-YOUTUBE-KEY;
    push rtmp://live.twitch.tv/app/YOUR-TWITCH-KEY;
}

Each push opens a separate outbound RTMP connection. This is fine for two or three destinations on a small VPS, but it eats upstream bandwidth fast — every destination is a full copy of your bitrate.

HTTP Callback Authentication

application live {
    live on;
    on_publish http://127.0.0.1:3000/auth;
    on_publish_done http://127.0.0.1:3000/done;
}

Nginx posts the stream name, IP, and metadata to your endpoint. Return HTTP 2xx to allow the publish, anything else to reject. This is how most production deployments enforce stream keys — the keys live in your application database, not in nginx.conf.

Multi-Bitrate via FFmpeg exec

application live {
    live on;
    exec ffmpeg -i rtmp://localhost/live/$name
      -c:v libx264 -b:v 2500k -s 1280x720 -f flv rtmp://localhost/hls/$name_720p
      -c:v libx264 -b:v 1000k -s 854x480 -f flv rtmp://localhost/hls/$name_480p
      -c:v libx264 -b:v 500k -s 640x360 -f flv rtmp://localhost/hls/$name_360p;
}

This forks an FFmpeg process per publish that creates 720p, 480p, and 360p renditions. It’s CPU-heavy — a four-core box will choke on more than two or three concurrent streams.

Streaming to Your Nginx RTMP Server

Once nginx is running, any RTMP-capable client can publish. The two most common are OBS and FFmpeg.

OBS Studio: Settings → Stream → Service “Custom” → Server rtmp://your-server/live → Stream Key anything. Click Start Streaming.

FFmpeg from a file:

ffmpeg -re -i video.mp4 -c:v copy -c:a aac -ar 44100 -ac 1 \
  -f flv rtmp://your-server/live/test

The -re flag tells FFmpeg to read at the file’s native frame rate, which is what makes it behave like a live source. The -c:v copy flag avoids re-encoding. Test playback from VLC: Media → Open Network Stream → rtmp://your-server/live/test.

For HLS playback, point hls.js or any browser HLS player at http://your-server:8080/hls/test.m3u8. There’s a 6–20 second latency depending on hls_fragment and hls_playlist_length, which is normal for HLS.

Limitations of Nginx RTMP

The module is reliable for what it does, but the gaps matter once you move past hobby use.

Original Repository Is Slow-Moving

The upstream arut/nginx-rtmp-module repo has 1,100+ open issues and infrequent releases. Most production users compile against a community fork or accept that bugs may persist for years. Compare that to a managed API where someone else owns the patches.

No WebRTC, No Sub-Second Latency

HLS bottoms out around 5–10 seconds end-to-end, even with one-second fragments. If you need real-time interaction, you’re looking at WebRTC instead — see our WebRTC vs RTMP breakdown for the trade-off.

Single-Server Scalability

A 4-core VPS handles 30–50 concurrent RTMP relays without transcoding. Add per-stream FFmpeg transcoding for ABR and that drops to 3–5 streams per box. Scaling beyond that means an origin-edge topology you build yourself, plus a CDN for live streaming in front for HLS distribution.

No Built-In ABR Manifest

hls on; produces a single-rendition .m3u8. Real adaptive bitrate streaming requires you to transcode multiple renditions (with FFmpeg exec) and manually write a master playlist that references each variant. There’s no nginx.conf directive that does this for you.

Security Surface

Without allow publish rules and HTTP callback auth, your RTMP endpoint is open to the world. There’s no built-in TLS for RTMP — you need a stunnel or HAProxy front-end for RTMPS. Compare with managed platforms that handle SRT protocol and RTMPS by default.

Codec Lock-In

H.264 and AAC are the only first-class codecs. H.265, AV1, and Opus all require careful FFmpeg exec workflows and may not play through the HLS packager without manual segment generation. Modern codecs covered in our HEVC vs H.264 post are essentially out of scope for stock nginx rtmp.

When to Use Nginx RTMP — and When to Use a Managed API

Self-hosting nginx rtmp pays off when bandwidth is cheap and you control both ends of the pipe. It breaks down when audiences span continents, when stream counts go over a few dozen, or when “the stream went down at 2am” becomes a business problem instead of a personal one.

Three rough heuristics:

  • Choose nginx rtmp when you have under 20 concurrent streams, viewers are in one region, you’re comfortable in nginx config, and downtime is acceptable.
  • Choose a hybrid setup (nginx rtmp ingest behind a CDN) when you need broader reach but want to keep encoder and authentication logic local.
  • Choose a managed RTMP API when you ship a product, your viewers are global, you need adaptive bitrate streaming automatically, and engineering time is more expensive than infrastructure.

LiveAPI fills the third bucket. The live streaming API gives you the same rtmp:// ingest endpoint you’d point OBS at — except it’s backed by automatic transcoding, ABR ladder generation, multi-CDN HLS delivery (Akamai, Cloudflare, Fastly), an embeddable HTML5 player, SRT vs RTMP ingest, webhooks, and live-to-VOD recording. There’s no nginx.conf, no FFmpeg exec chains, and no firewall rules to maintain. Pricing is per-minute, so you only pay for streams that actually run.

For teams choosing between rolling their own and using an API, our best live streaming APIs post compares the major options side by side.

Nginx RTMP FAQ

Is nginx rtmp still maintained?

The original arut/nginx-rtmp-module repo on GitHub receives infrequent updates, but several active forks (notably the one by sergey-dryabzhinsky) keep it building against current nginx releases. The Ubuntu libnginx-mod-rtmp package usually tracks one of these forks.

Can nginx rtmp do RTMPS?

Not directly — the module only speaks plain RTMP on TCP 1935. To accept RTMPS publishes, run stunnel or HAProxy in front of nginx, terminating TLS on port 443 and forwarding plain RTMP to localhost:1935. Some FFmpeg builds with --enable-openssl can publish RTMPS; check ffmpeg -protocols.

How many concurrent streams can nginx rtmp handle?

A 4-core, 8 GB VPS handles 30–50 concurrent RTMP relays (no transcoding) comfortably. With per-stream FFmpeg transcoding for ABR, expect 3–5 streams per box. Bandwidth is usually the limit before CPU on cloud VPS instances.

Does nginx rtmp work on Windows?

Yes, but with caveats. The module compiles on Windows, but exec directives, static pulls, and auto_push are not supported. For Windows development, most people run nginx rtmp inside Docker or WSL2 instead of native Windows.

How do I generate stream keys for nginx rtmp?

Stream keys are just the path component of the RTMP URL — rtmp://server/live/ is the key. To validate them, point on_publish at your application server, look up the key in your database, and return HTTP 200 to accept or 403 to reject.

What’s the difference between RTMP and RTMPS in nginx?

RTMP is plain TCP on port 1935. RTMPS adds TLS, usually on port 443. Nginx rtmp only handles RTMP natively; you wrap it in stunnel for RTMPS. Both speak the same FLV-tagged H.264/AAC payload — only the transport differs.

Can nginx rtmp output to MPEG-DASH?

Yes — set dash on;, dash_path /var/www/dash;, and serve the DASH directory over HTTP, similar to the HLS setup. DASH is useful for browsers without HLS support, though most modern players handle both.

Is nginx rtmp free?

The module is BSD-2-Clause licensed and free to use commercially. Costs come from the VPS or bare-metal server you run it on, plus bandwidth — both of which scale linearly with stream count and viewer count.

Closing Thoughts

The nginx rtmp module is the cheapest way to get a working RTMP ingest endpoint with HLS output, and for small projects it’s still the right choice. The catch is that everything past “one stream, a few viewers, one region” turns into work you have to do yourself: ABR transcoding, multi-region delivery, RTMPS, monitoring, recording rotation, and bug patches the upstream repo hasn’t merged.

If you’ve already built the second version of that work and you’re tired of maintaining it, Get started with LiveAPI — it gives you the same RTMP ingest URL with managed transcoding, multi-CDN delivery, and a player baked in.

Join 200,000+ satisfied streamers

Still on the fence? Take a sneak peek and see what you can do with Castr.

No Castr Branding

No Castr Branding

We do not include our branding on your videos.

No Commitment

No Commitment

No contracts. Cancel or change your plans anytime.

24/7 Support

24/7 Support

Highly skilled in-house engineers ready to help.

  • Check Free 7-day trial
  • CheckCancel anytime
  • CheckNo credit card required