Skip to content

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:

  1. RTMP Application Config - build/conf/nginx/http.d/apps/<SERVICE>.conf
  2. Optional Transformer - build/conf/nginx/http.d/transformers/<SERVICE>.conf
  3. Pre-init Script - build/scripts/pre-init.d/90_configure_<SERVICE>.sh
  4. Environment Variables - Configures the service and its behavior. Defined in Dockerfile and can be overridden in env/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

  1. 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
  2. Application Routing: The relay application receives the stream
  3. Authorization: IP-based authentication checks PUBLISH_IP_RANGE
  4. Service Processing:
  5. Simple Relay Services (YouTube, Twitch partner mode): Stream pushed directly to destination
  6. Transformer Services (Twitch non-partner mode): an exec directive in relay runs FFmpeg, which re-encodes the stream and publishes it to the local twitch application. That application pushes it to Twitch
  7. 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:

/scripts/enableService.sh twitch

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:

      include /etc/nginx/http.d/transformers/twitch.conf;
    ...
    include /etc/nginx/http.d/apps/twitch.conf;

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:

push rtmp://TWITCH_ENDPOINT.contribute.live-video.net/app/TWITCH_KEY;

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:

push rtmp://use10.contribute.live-video.net/app/live_123456789_abc;

13.6.2 Layered Defaults

  1. Dockerfile - Defaults for all variables
  2. env/relay.env - User overrides, loaded by docker compose through env_file in docker-compose.yml. A plain docker run does 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