Nextcloud Talk, STUN & TURN

Talk (Paketname spreed) ist die integrierte Video-, Audio- und Chat-Lösung in Nextcloud. Talk basiert auf WebRTC, läuft direkt im Browser und arbeitet standardmässig im Peer-to-Peer-Modus: jeder Client sendet seinen Media-Stream an jeden anderen Teilnehmer.

Alle Komponenten des Talk-Stacks sind Open Source (AGPL-3.0). Eine Nextcloud-Subscription ist für den Betrieb nicht erforderlich, bietet aber vorgefertigte Pakete, Support mit SLA sowie Zugang zur SIP-Bridge (Telefon-Einwahl).

Begriffe

HPB (High Performance Backend)

Sammelbegriff aus der Nextcloud-Doku für den Verbund aus Signaling-Server, Janus Gateway und NATS, der das in der Talk-App integrierte PHP-basierte Signaling durch eine performante Standalone-Lösung ersetzt. HPB ist kein eigenes Softwarepaket, die drei Komponenten werden einzeln installiert.

ICE (Interactive Connectivity Establishment)

Verfahren, mit dem der Browser automatisch den besten Verbindungsweg ermittelt. ICE probiert zuerst eine direkte Verbindung, dann mit Hilfe von STUN, und greift als letzten Ausweg auf TURN zurück. Passiert transparent für den Benutzer.

SFU (Selective Forwarding Unit)

Architekturkonzept, bei dem ein zentraler Server den Media-Stream jedes Teilnehmers entgegennimmt und an alle anderen weiterleitet, ohne ihn zu verändern (kein Transcoding). Im Talk-Stack übernimmt Janus diese Rolle.

STUN (Session Traversal Utilities for NAT)

Hilft Clients, ihre öffentliche IP hinter NAT zu erkennen. Der Client schickt ein kleines UDP-Paket an den STUN-Server, der mit der öffentlichen IP und dem Port antwortet, die der NAT-Router vergeben hat. Der Client teilt diese Adresse dem Gegenüber mit, damit eine direkte Verbindung aufgebaut werden kann. Leichtgewichtig: der Server wird nur beim Verbindungsaufbau kurz gefragt. STUN ist eine Teilfunktion des TURN-Servers.

TURN (Traversal Using Relays around NAT)

Relayed den gesamten Media-Traffic, wenn eine direkte Verbindung nicht möglich ist (restriktive Firewalls, symmetrisches NAT, Deep Packet Inspection). Im Gegensatz zu STUN fliesst bei TURN der komplette Audio/Video-Stream über den Server und verbraucht entsprechend Bandbreite.

WebRTC

Transportiert Audio- und Video-Daten als UDP-Streams (RTP/RTCP). UDP, weil bei Echtzeit-Media die Geschwindigkeit wichtiger ist als garantierte Zustellung.

WebSocket

Persistente TCP-Verbindung für Steuerungsinformationen (Signaling), z.B. „User X ist dem Call beigetreten“ oder „Hier ist meine SDP-Beschreibung für den Verbindungsaufbau“. Läuft über Port 443 wie normaler HTTPS-Traffic.

Komponenten und Software

coturn

STUN- und TURN-Server in einem. https://github.com/coturn/coturn. Alternative: eturnal (https://github.com/processone/eturnal).

Janus Gateway

Implementierung eines WebRTC-Gateways, entwickelt von Meetecho (https://github.com/meetecho/janus-gateway). Übernimmt im Talk-Stack die Rolle der SFU.

NATS

Schlanker Message-Broker (vergleichbar mit Redis Pub/Sub oder RabbitMQ). Der Signaling-Server nutzt NATS intern als Message-Bus. Bei einem einzelnen Signaling-Server läuft die Kommunikation nur lokal; bei mehreren Instanzen (Clustering) synchronisiert NATS die Signaling-Server untereinander. Software: nats-server, ein einzelnes Go-Binary (https://github.com/nats-io/nats-server).

Signaling-Server

nextcloud-spreed-signaling, ein Go-Binary von der Struktur AG (https://github.com/strukturag/nextcloud-spreed-signaling). Zentrale Komponente des HPB, die über WebSocket mit den Clients kommuniziert und den Verbindungsaufbau zwischen den Teilnehmern koordiniert. Authentifiziert Clients per Shared Secret gegen den Nextcloud-Server.

Mesh vs. SFU

Ohne HPB arbeitet Talk im Mesh-Modus: jeder Client baut eine direkte WebRTC-Verbindung zu jedem anderen Teilnehmer auf. Die benötigte Upload-Bandbreite pro Benutzer liegt bei ca. 1 Mbit/s × (Anzahl Teilnehmer - 1). Das praktische Limit liegt bei 4 bis 5 Benutzern mit Video, bei Audio-only ca. 20.

Mit HPB sendet jeder Client nur einen einzigen Stream an Janus (die SFU), und Janus verteilt ihn an die übrigen Teilnehmer. Das skaliert auf 30 bis 50 aktive Video-Teilnehmer und hunderte passive Zuschauer. Das HPB lohnt sich bereits ab 3 Teilnehmern (halbiert die Upload-Bandbreite) und ist ab 4 bis 5 Teilnehmern praktisch Pflicht.

Mesh (ohne HPB):              SFU (mit HPB):

A ◄──► B                      A ──► ┌─────┐ ──► B
│ ╲  ╱ │                      B ──► │ SFU │ ──► A
│  ╲╱  │                      C ──► │     │ ──► alle
│  ╱╲  │                      D ──► └─────┘ ──► alle
│ ╱  ╲ │
C ◄──► D

Aufbau

Für eine kleine Umgebung mit weniger als 5 Benutzern pro Call:

  • Nextcloud Talk App aus dem App Store installieren.

  • Ein STUN-Server ist vorkonfiguriert (Nextcloud GmbH betreibt einen öffentlichen Server).

  • Einen TURN-Server (coturn oder eturnal) für den Produktivbetrieb dringend empfohlen.

Für ein vollständiges Produktions-Setup ab 5 Benutzern pro Call zusätzlich (diese drei Komponenten bilden zusammen das HPB):

  • Signaling-Server (nextcloud-spreed-signaling)

  • Janus Gateway (WebRTC-SFU)

  • NATS-Server (Message-Bus)

Der TURN-Server ist kein Bestandteil des HPB. TURN dient als Fallback-Relay, wenn Clients Janus nicht direkt per UDP erreichen können, und wird unabhängig vom HPB betrieben. In der Praxis laufen beide oft auf demselben Server.

Installationsoptionen:

  • Paketierte Distributionen: nextcloud-spreed-signaling liegt als Paket in den offiziellen Debian-Repos; für RHEL gibt es keine Upstream-Pakete - dort aus Source bauen (Go >= 1.25, make build). Für coturn existiert die LFOps-Rolle coturn; für den Signaling-Server muss aktuell selbst gepackt werden.

  • Container: ghcr.io/nextcloud-releases/aio-talk bündelt alle drei HPB-Komponenten plus TURN.

  • All-in-One: Nextcloud AIO (Docker-basiertes Deployment mit allen HPB-Komponenten und TURN).

Verbindungsaufbau

Der Browser unterhält zwei parallele Verbindungen: eine WebSocket-Verbindung für das Signaling und eine WebRTC-Verbindung für die Audio/Video-Daten.

  1. Der Browser öffnet Nextcloud und startet einen Call.

  2. Der Browser baut eine WebSocket-Verbindung zum Signaling-Server auf (TCP 443, über den Reverse Proxy). Darüber wird ausgehandelt, wer im Call ist und wie die Media-Verbindung aufgebaut wird.

  3. Der Browser baut eine WebRTC-Verbindung zu Janus auf (UDP 20000-40000). Darüber fliessen Audio und Video.

  4. Falls die direkte UDP-Verbindung zu Janus nicht klappt (Firewall, NAT), wird über das ICE-Verfahren automatisch der TURN-Server als Relay dazwischengeschaltet.

                      Internet
                         │
             ┌───────────▼────────────┐
             │       Firewall         │
             │      (Public IP)       │
             │                        │
             │  TCP 443 ──────────────────► Reverse Proxy
             │  TCP+UDP 3478 ─────────────► TURN-Server
             │  UDP 20000-40000 ──────────► Janus (SFU)
             └────────────────────────┘

             ┌────────────────────────┐
             │     Reverse Proxy      │
             │       (Apache)         │
             │                        │
             │  /nextcloud ────────────────► Nextcloud-Server
             │  /standalone-signaling/ ────► Signaling-Server
             │  (inkl. WebSocket-Upgrade)   (Port 8080, lokal)
             └────────────────────────┘

┌──────────────────────┐          ┌──────────────────────────┐
│   Nextcloud-Server   │          │    HPB + TURN-Server     │
│                      │          │                          │
│  Nextcloud + Talk App│◄─HTTP──► │  Signaling-Server :8080  │
│  PHP-FPM             │  API     │  Janus Gateway    :8188  │
│  PostgreSQL/MariaDB  │(shared   │  NATS             :4222  │
│  Redis               │ secret)  │  coturn           :3478  │
└──────────────────────┘          └──────────────────────────┘

Firewall

Public nach aussen öffnen:

Port

Protokoll

Zweck

443

TCP

Nextcloud Web-UI + Signaling via Reverse Proxy (WebSocket)

3478

TCP+UDP

TURN-Server (Port 443 wäre für Clients hinter restriktiven Firewalls besser, kollidiert aber mit TCP 443 des Reverse Proxy auf derselben IP; Ausweg: zweite Public IP oder coturn auf UDP 443)

20000-40000

UDP

Janus WebRTC Media (RTP/RTCP)

Nur intern/localhost auf dem HPB + TURN-Server:

Port

Protokoll

Zweck

4222

TCP

NATS (Message-Bus)

8080

TCP

Signaling-Server (hinter Reverse Proxy)

8188

TCP

Janus WebSocket API

Reverse Proxy (Apache)

Der Signaling-Server kommuniziert über WebSocket. Apache benötigt dafür mod_proxy_wstunnel.

<Location /standalone-signaling/>
    ProxyPass http://p-talk01:8080/standalone-signaling/
    ProxyPassReverse http://p-talk01:8080/standalone-signaling/

    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteCond %{HTTP:Connection} upgrade [NC]
    RewriteRule ^(.*)$ ws://p-talk01:8080/$1 [P,L]
</Location>

Ob der Signaling-Server korrekt erreichbar ist, lässt sich prüfen mit:

curl --include https://cloud.example.com/standalone-signaling/api/v1/welcome
# Expected: {"nextcloud-spreed-signaling":"Welcome","version":"..."}

Best Practices

  • HTTPS ist Pflicht, da WebRTC ohne TLS nicht funktioniert.

  • Kein SQLite als Nextcloud-Datenbank verwenden, nur MySQL/MariaDB oder PostgreSQL.

  • Apache muss PHP-FPM mit mpm_event oder PHP mit mpm_prefork verwenden. Andere Kombinationen brechen das Signaling.

  • TURN auf Port 443 zu konfigurieren ist ratsam, auch ohne TLS, da viele Firewalls nur Port 443 nach aussen zulassen. Falls der Reverse Proxy bereits TCP 443 belegt, hilft eine zweite Public IP oder coturn auf UDP 443 (UDP kollidiert nicht mit TCP auf demselben Port).

  • coturn ab Version 4.5.0.8 verwenden. Ältere Versionen haben einen Bug, bei dem IPv6-UDP-Sockets nicht freigegeben werden.

  • Damit coturn als unprivilegierter Benutzer auf Port 443 binden kann: setcap cap_net_bind_service=+ep /usr/bin/turnserver.

  • In den Nextcloud-Talk-Admin-Settings (/settings/admin/talk) keine Protokoll-Präfixe wie turn: oder https:// eingeben, nur domain:port. Das Protokoll wird per Dropdown gewählt.

  • Das Shared Secret zwischen Signaling-Server (server.conf, Sektion [backend]) und Nextcloud (Talk-Admin-Settings unter „External signaling server“) muss übereinstimmen. Einen starken Schlüssel erzeugen mit openssl rand -hex 32.

  • In der Janus-Konfiguration (janus.jcfg) muss broadcast = true in der events-Sektion gesetzt sein, und in janus.eventhandler.wsevh.jcfg muss der Event-Handler aktiviert sein. Ohne diese Konfiguration kann der Signaling-Server den Media-Status nicht verfolgen.