Smartphone, Kameradrohne und Action-Cam streamen per RTMP zum Relay-Server, OBS holt das Signal ab und verteilt es an Twitch, YouTube und weitere Plattformen
Für diese Serverversionen erstellt: Ubuntu Server 26.04 LTS und Debian 13 „Trixie“. Die Anleitung verwendet die Pakete und Befehle aus den offiziellen Paketquellen dieser Versionen. Unter älteren Ausgaben wie Ubuntu 24.04 LTS oder Debian 12 können viele Schritte ebenfalls funktionieren, sie sind hier jedoch nicht die verbindliche Grundlage.

Ziel dieser Anleitung

Moblin, OBS oder eine IRL-Kamera sendet per RTMP an deinen Linux-Server. Nginx akzeptiert den Stream nur mit einem langen geheimen Stream-Key. Ein interner HTTP-Endpunkt prüft den Key über on_publish; er ist ausschließlich unter 127.0.0.1:8080 erreichbar.

Sicherheitsprinzip: Jeder Publish-Versuch wird über on_publish geprüft. Port 8080 bleibt intern; die Statusseite läuft getrennt auf Port 8081.
Moblin / OBS → RTMP :1935 → Nginx-RTMP → OBS / Weiterverarbeitung
                               │
                               └→ interne Key-Prüfung 127.0.0.1:8080

Inhalt

1. Linux-Server vorbereiten

Die Befehle passen für Ubuntu Server und Debian. Aktualisiere zuerst das System und installiere Nginx samt RTMP-Modul, die deutschen Sprach- und Ländereinstellungen sowie Fail2Ban:

sudo apt update
sudo apt upgrade -y
sudo apt install nginx libnginx-mod-rtmp locales fail2ban -y
sudo systemctl enable --now nginx
sudo systemctl status nginx

Der Nginx-Status sollte active (running) anzeigen.

Deutsche Sprache, UTF-8 und Ländereinstellungen aktivieren

de_DE.UTF-8 stellt die Standardsprache und landestypische Formate auf Deutsch (Deutschland) um. Dazu gehören beispielsweise Datums-, Zahlen-, Währungs- und Sortierformate. UTF-8 sorgt dafür, dass Umlaute und Sonderzeichen korrekt verarbeitet werden.

sudo sed -i '/de_DE.UTF-8/s/^# *//' /etc/locale.gen
sudo locale-gen
sudo update-locale LANG=de_DE.UTF-8 LANGUAGE=de_DE:de
sudo timedatectl set-timezone Europe/Berlin

Melde dich danach einmal ab und wieder an. Kontrolliere anschließend die Spracheinstellungen und die Zeitzone:

locale
timedatectl
Erwartetes Ergebnis: Bei LANG sollte de_DE.UTF-8 stehen und als Zeitzone Europe/Berlin angezeigt werden. Bereits geöffnete Terminals übernehmen die neue Sprache erst nach einer erneuten Anmeldung.

Fail2Ban für SSH aktivieren

Fail2Ban beobachtet fehlgeschlagene SSH-Anmeldungen und sperrt auffällige IP-Adressen vorübergehend. Die folgende kleine Zusatzkonfiguration erlaubt fünf Fehlversuche innerhalb von zehn Minuten und sperrt die betreffende Adresse danach für eine Stunde:

sudo tee /etc/fail2ban/jail.d/sshd.local > /dev/null <<'EOF'
[sshd]
enabled = true
port = ssh
maxretry = 5
findtime = 10m
bantime = 1h
EOF

sudo systemctl enable --now fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd

Die Statusausgabe zeigt unter anderem die Anzahl fehlgeschlagener Versuche und aktuell gesperrte IP-Adressen. Fail2Ban ist ein zusätzlicher Schutz; am sichersten bleibt SSH mit Schlüsselanmeldung statt Passwort.

Wichtig: Prüfe vor dem Abmelden, dass dein SSH-Zugang funktioniert. Wiederholte falsche Anmeldungen können sonst auch deine eigene IP-Adresse vorübergehend sperren.

2. Sicheren Stream-Key erstellen

Erzeuge einen zufälligen 64-stelligen Schlüssel. Verwende keine einfachen Werte wie test, stream oder 12345.

openssl rand -hex 32

Beispiel: 8e71c20a29593b84f31ed8c4ae365318bd720490114565168c526f018847162a

Wichtig: Der Beispiel-Key ist öffentlich. Erzeuge deinen eigenen, bewahre ihn wie ein Passwort auf und zeige ihn nicht im Stream oder in Screenshots.

3. Original-Konfiguration sichern

sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup

Bei Bedarf stellst du sie so wieder her:

sudo cp /etc/nginx/nginx.conf.backup /etc/nginx/nginx.conf

4. Nginx mit Publish-Authentifizierung konfigurieren

Öffne /etc/nginx/nginx.conf und ersetze den Platzhalter durch deinen eigenen Key:

Fertige nginx.conf herunterladen

Im Terminal ausführen sudo nano /etc/nginx/nginx.conf
user www-data;
worker_processes 1;
pid /run/nginx.pid;

include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;

    map $arg_name $rtmp_publish_allowed {
        default 0;
        "HIER_DEINEN_STREAM_KEY_EINTRAGEN" 1;
    }

    server {
        listen 127.0.0.1:8080;
        server_name localhost;

        access_log /var/log/nginx/rtmp-auth-access.log;
        error_log /var/log/nginx/rtmp-auth-error.log;

        location = /publish-auth {
            if ($rtmp_publish_allowed = 0) {
                return 403;
            }
            return 200;
        }
    }

    server {
        listen 8081;
        server_name _;

        access_log /var/log/nginx/rtmp-stat-access.log;
        error_log /var/log/nginx/rtmp-stat-error.log;

        location = / { return 302 /stat; }

        location = /stat {
            rtmp_stat all;
            rtmp_stat_stylesheet stat.xsl;
            add_header Cache-Control "no-store" always;
        }

        location = /stat.xml {
            rtmp_stat all;
            add_header Cache-Control "no-store" always;
        }

        location = /stat.xsl {
            root /usr/share/nginx/html;
        }
    }
}

rtmp {
    server {
        listen 1935;
        chunk_size 4096;

        application live {
            live on;
            record off;
            notify_method get;
            on_publish http://127.0.0.1:8080/publish-auth;
            allow publish all;
            allow play all;
        }
    }
}

on_publish übergibt den Streamnamen als Parameter name. Die map erlaubt nur den eingetragenen Key; alle anderen Werte erhalten 403 Forbidden. worker_processes 1; ist für diesen Aufbau entscheidend: Bei mehreren Workern können Publisher, OBS und Statistik in unterschiedlichen Prozessen landen und den aktiven Stream nicht finden.

In Nano speicherst du mit Strg+O, Enter und beendest mit Strg+X.

4a. Formatierte RTMP-Statusseite installieren

Die Statusseite zeigt Server-Uptime, Bandbreite, Bytes In/Out, Streamname, Publisher, Clients, Video- und Audiodaten sowie die Stream-Laufzeit.

Fertige stat.xsl herunterladen

sudo cp stat.xsl /usr/share/nginx/html/stat.xsl
sudo chmod 644 /usr/share/nginx/html/stat.xsl
sudo nginx -t
sudo systemctl restart nginx

Formatierte Übersicht: http://DEINE-SERVER-IP:8081/stat
Rohe XML-Daten für NOALBS: http://DEINE-SERVER-IP:8081/stat.xml

Vorläufig öffentlich: Port 8081 wird in dieser Anleitung vollständig freigegeben. Der Stream-Key kann als Streamname in der Statistik erscheinen. Ergänze deshalb später unbedingt einen geeigneten Zugriffsschutz.

5. Konfiguration prüfen und Nginx neu starten

sudo nginx -t

Starte Nginx nur neu, wenn syntax is ok und test is successful erscheinen:

sudo systemctl restart nginx
sudo systemctl status nginx

6. Firewall absichern

Reihenfolge beachten: Erlaube zuerst SSH, damit du dich nicht vom Server aussperrst.
sudo apt install ufw -y
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 1935/tcp
sudo ufw allow 8081/tcp
sudo ufw enable
sudo ufw status

Öffne Port 8080 nicht. Port 8081 wird mit dieser Regel dagegen für alle Quelladressen freigegeben, damit die Statistik extern erreichbar ist. Weitere Schutzmaßnahmen können später ergänzt werden.

7. Listener und Bindings kontrollieren

sudo ss -tulpn | grep 1935
sudo ss -tulpn | grep 8080
sudo ss -tulpn | grep 8081

RTMP darf auf 0.0.0.0:1935 lauschen. Für die Authentifizierung muss ausdrücklich 127.0.0.1:8080 erscheinen. Die Statusseite lauscht öffentlich auf Port 8081.

8. OBS als Sender konfigurieren

Öffne Einstellungen → Stream und wähle den Dienst Benutzerdefiniert.

Server:     rtmp://DEINE-SERVER-IP/live
Stream-Key: DEIN_GEHEIMER_STREAM_KEY

Die vollständige Adresse ergibt sich als rtmp://DEINE-SERVER-IP/live/DEIN_GEHEIMER_STREAM_KEY.

9. Moblin konfigurieren

Lege in Moblin ein benutzerdefiniertes RTMP-Ziel an:

RTMP-Adresse: rtmp://DEINE-SERVER-IP/live
Stream-Key:   DEIN_GEHEIMER_STREAM_KEY

Der Datenweg lautet dann: iPhone → Moblin → Linux-RTMP-Server → OBS.

10. Stream in OBS empfangen

Füge auf dem Empfangs-PC eine Medienquelle oder eine FFmpeg-kompatible Quelle hinzu und verwende:

rtmp://DEINE-SERVER-IP/live/DEIN_GEHEIMER_STREAM_KEY

Von dort kann OBS das Signal weiterverarbeiten und beispielsweise an Twitch, YouTube oder Kick senden.

11. Richtigen und falschen Stream-Key testen

  1. Starte einen Test mit einem absichtlich falschen Key wie falscherkey. Publishing muss scheitern.
  2. Wiederhole den Test mit deinem echten Key. Die Verbindung muss funktionieren.
  3. Prüfe zusätzlich, dass Port 8080 aus dem Internet nicht erreichbar ist.
Erwartetes Ergebnis: Falscher Key wird mit 403 abgewiesen; nur der eingetragene Key darf senden.

12. Logs und Fehlerdiagnose

Beobachte bei Verbindungsproblemen die Nginx-Fehler und den Dienststatus live:

sudo tail -f /var/log/nginx/error.log
sudo journalctl -u nginx -f

Kontrolliere außerdem Tippfehler im Key, die öffentliche Server-IP, die UFW-Regel und ob dein Hosting-Anbieter TCP 1935 zusätzlich in einer externen Firewall freigeben muss.

13. Autostart nach einem Neustart prüfen

sudo systemctl enable nginx
sudo reboot

Nach dem erneuten Anmelden:

sudo systemctl status nginx
sudo ss -tulpn | grep 1935

14. Sicherheitsübersicht

KomponenteErreichbarkeitSchutz
SSH, TCP 22Öffentlich, falls benötigtUFW; idealerweise SSH-Key statt Passwort
RTMP, TCP 1935ÖffentlichLanger, zufälliger Publish-Key via on_publish
Auth, TCP 8080Nur 127.0.0.1Nicht in UFW freigeben
Status, TCP 8081ÖffentlichDerzeit vollständig freigegeben; Zugriffsschutz später ergänzen
AufzeichnungenDeaktiviertrecord off;
Internet
  ├── TCP 22   → SSH
  ├── TCP 1935 → RTMP → on_publish → Key-Prüfung
  └── TCP 8080 ✕ blockiert
                       └→ nur intern: 127.0.0.1:8080

Wer den RTMP-Port findet, kann ohne den geheimen Key trotzdem keinen Stream veröffentlichen. Beachte: allow play all; schützt den Abruf nicht. Wenn auch das Ansehen privat sein soll, benötigst du zusätzlich eine Play-Authentifizierung oder einen separaten privaten Transportweg.