13. Architecture
This document explains the internal architecture of docker-rtmp-multistream and how it processes and distributes streams.
13.1 Overview
docker-rtmp-multistream is built on nginx with the Real-Time Messaging Protocol (RTMP) module. It receives a single RTMP stream from your streaming software and simultaneously distributes it to multiple destinations with optional per-service transformations.
13.2 Core Components
13.2.1 nginx RTMP Module
The foundation is nginx-mod-rtmp, which provides RTMP server capabilities to nginx. This module handles:
- Receiving RTMP streams on port 1935
- Managing multiple RTMP applications
- Pushing streams to multiple destinations
- Recording streams to disk
- Executing FFmpeg transformers
13.2.2 Service-Based Architecture
Each streaming destination (service) consists of modular components:
- RTMP Application Config -
build/conf/nginx/http.d/apps/<SERVICE>.conf - Optional Transformer -
build/conf/nginx/http.d/transformers/<SERVICE>.conf - Pre-init Script -
build/scripts/pre-init.d/90_configure_<SERVICE>.sh - Environment Variables - Configures the service and its behavior. Defined in
Dockerfileand can be overridden inenv/relay.env
Services are enabled/disabled dynamically at container startup based on configuration.
13.3 Stream Flow
13.3.1 High-Level Flow
Streaming software (for example OBS Studio)
↓
RTMP stream (port 1935)
↓
relay application
↓
┌──────────────┬───────────┬───────────┐
↓ ↓ ↓ ↓
Twitch Twitch YouTube Archive
non-partner partner (relay) (record)
(FFmpeg →
twitch app) (relay)
Twitch runs in one of the two modes, never both: TWITCH_PARTNER selects which.
13.3.2 Detailed Request Flow
- Stream Reception: Your streaming software connects to
rtmp://<RELAY_HOST>:1935/relay/<STREAM_NAME>. The relay accepts any stream name. It is not a service stream key: do not put your Twitch or YouTube key here - Application Routing: The
relayapplication receives the stream - Authorization: IP-based authentication checks
PUBLISH_IP_RANGE - Service Processing:
- Simple Relay Services (YouTube, Twitch partner mode): Stream pushed directly to destination
- Transformer Services (Twitch non-partner mode): an
execdirective inrelayruns FFmpeg, which re-encodes the stream and publishes it to the localtwitchapplication. That application pushes it to Twitch - Archive Service: Stream recorded to local disk
13.4 Service Patterns
docker-rtmp-multistream supports two architectural patterns for handling streams: Simple Relay and Transformer.
For a complete comparison of these patterns (use cases, pros/cons, and examples), see Service Patterns Reference.
13.4.1 Technical Implementation
Simple Relay (e.g., YouTube): Single apps/<SERVICE>.conf file, included inside application relay, that pushes the stream directly to the destination without modification.
Transformer (e.g., Twitch non-partner mode): Two-stage pipeline. transformers/<SERVICE>.conf (FFmpeg) is included inside application relay. apps/<SERVICE>.conf defines a separate application, outside relay, that receives FFmpeg's output and pushes it to the destination.
Conditional Patterns
Some services support both patterns based on configuration. Twitch uses simple relay for partners (TWITCH_PARTNER=TRUE) and transformer for non-partners. See Twitch Configuration.
13.5 Configuration System
13.5.1 Startup Flow
When the container starts, configuration happens in this order:
1. Docker starts the container with the Dockerfile defaults, overridden by
env/relay.env when you start it with docker compose (docker-compose.yml
passes it as env_file)
2. build/scripts/run.sh runs each pre-init script in alphanumeric order:
a. 89_configure_app.sh - validates PUBLISH_IP_RANGE and the log level,
fills in app.conf and auth.conf
b. 90_configure_*.sh - one per service
3. Each service script:
- Exits 0 without changes if its required variable is empty
- Validates its variables, and exits 1 if one is invalid
- Uses sed to replace placeholders in its config
- Calls enableService.sh to activate the service
4. If any script exits non-zero, run.sh stops the container
5. Otherwise nginx starts with the active services
13.5.2 Configuration Files
Main Configuration:
- nginx.conf - Loads RTMP module, includes app.conf
- app.conf - Defines relay application, commented service includes
- auth.conf - IP-based publish authentication
Service Configurations:
- apps/*.conf - Individual service RTMP applications
- transformers/*.conf - FFmpeg transcoding pipelines
13.5.3 Dynamic Service Enabling
Service includes are commented out in build/conf/nginx/http.d/app.conf. The Twitch non-partner lines are:
application relay {
...
# Twitch (Non-Partner - Transformer Pattern)
#include NGINX_CONFD_DIR/transformers/twitch.conf;
...
}
# Twitch (Non-Partner - Transformer Destination)
#include NGINX_CONFD_DIR/apps/twitch.conf;
At startup, 89_configure_app.sh replaces NGINX_CONFD_DIR with its value, /etc/nginx/http.d. 90_configure_twitch.sh then runs:
enableService.sh removes the # from #include <NGINX_CONFD_DIR>/apps/twitch.conf, and from the matching transformers/twitch.conf line if that file exists. Result:
The match is on #include with no space, so a new service's lines in app.conf must use the same form.
13.6 Environment Variable Processing
13.6.1 Template Variables
Configuration files use bare placeholder tokens, named after the variable, that are replaced at startup.
In build/conf/nginx/http.d/apps/twitch.conf:
In 90_configure_twitch.sh, after validation, each value is escaped and substituted with | as the sed delimiter:
TWITCH_KEY_ESC=$(escape_for_sed "$TWITCH_KEY")
sed -i "s|TWITCH_KEY|$TWITCH_KEY_ESC|g" "${NGINX_CONFD_DIR}/apps/twitch.conf"
After processing, with TWITCH_ENDPOINT=use10:
13.6.2 Layered Defaults
- Dockerfile - Defaults for all variables
- env/relay.env - User overrides, loaded by
docker composethroughenv_fileindocker-compose.yml. A plaindocker rundoes not read it unless you pass--env-file env/relay.env
13.7 Security
For publish authorization with PUBLISH_IP_RANGE, input validation, where stream keys end up, and what is not protected, see Security.
13.8 Archive Service
The Archive service modifies the main relay application rather than creating a separate app. 90_configure_archive.sh deletes record off; from app.conf and enables this include inside application relay:
recorder all {
record all;
record_path ARCHIVE_PATH;
record_unique on;
record_suffix _%d%m%Y_%H%M%S.flv;
record_notify on;
}
ARCHIVE_PATH is replaced with its value at container start. For the resulting file names, see File Naming.
Every stream published to the relay application is archived, whichever services are enabled. Streams published directly to the twitch application are not.
13.9 See Also
- Service Patterns Reference - Detailed comparison of architectural patterns
- Change Relay Settings - Setup and environment variables
- Security - Publish authorization, validation, and what is not protected
- Add a Streaming Service - Implement new streaming services