Nextcloud Talk High Performance Backend (HPB) - Multi-Domain Setup Guide

below a draft of a very similar article I planned for long time. maybe over time we can combine both into a best practice running HPB as container

Talk HPB overview

Long planned article and implementation of Nextcloud Talk HPB. I’m doing it hard way with all components configured one by one. this is little harder than simply adding AiO Talk container (look at High Performance Backend for Talk on Nextcloud with Docker for details) which does the job a s well but I don’t like balck boxes and prefer to know what is inside and how it works. this manual way allows more control about individual parts and gives your understanding how they work together.

I had really hard time understanding how it works as almost no docs exist and things are not really hidden but not documented well too. In fact the integration turns out pretty simple with 3 (or 4) components required. Start with the turn server which is a must for each Talk installation. I’m using coturn but there are alternatives and I think there is no real difference. TURN server is workhorse doing hard job connecting clients in the internet bypassing firewall NAT, even CG-NAT to make client talk to each other.

Once the TURN server is in place participants can talk to each other but one problem remains - each client must send it’s media to every other meeting participant.. which becomes problematic if cpu power and bandwidth is limited. this is where the Talk HPB comes into play - it works as central instance receiving one media stream from each participant multiplexing it with other streams and sending one stream to each other participant. this components is known as MCU in other communication systems. HPB consists of 3 components: signalling server, janus and nats (which might be optional for small installations).

archticture

flowchart LR
  fritz.box-- port forward<br>udp/3478<br>udp/50000-500100 -->TURN;
  fritz.box-- port forward<br>http tcp/80<br>https tcp/443 -->RP;
  subgraph fritzbox
   fritz.box(router<br>192.168.179.1);
  end
  subgraph srv
		subgraph nc.mydomain.tld
		  NC[app];
		  NCNW([more comonents like db, redis...]);
		end
		subgraph dev-nc.mydomain.tld
		  DEV-NC[app];
		  DEV-NCNW([more comonents like db, redis...]);
		end
        subgraph talk-hpb
		  TURN[coturn]
		  SIGNALLING(talk-hpb)
		  NATS
		  JANUS
		  SIGNALLING & JANUS -- turn:corurn:3478 --> TURN
		  SIGNALLING -- ws://nats://nats:4222 --> NATS
		  SIGNALLING -- ws://janus:8188 --> JANUS
		end

		RP-- https:// ${TALKHPB_FQDN} -->SIGNALLING
		RP-- https -->NC & DEV-NC
		
	    TURN <-.-> NC & DEV-NC
		RP[reverse proxy<br>:80 + :443] 
	end

configuration

below compose file creates the “talk-hpb” part of the above architecture image. strictly speaking coturn is not part of an HPB but both are required for Talk and could be shared for multiple independent Nextcloud installations.

global config .env

this files contains global settings and settings shared between services

# .env
#TURN server
COTURN_SECRET=<<<shared TRUN secret>>>
COTURN_FQDN=turn.mydomain.tld
TALKHPB_FQDN=talk-hpb.mydomain.tld
NATS_VERSION=2.11.7       # omit for :latest tag
SIGNALLING_VERSION=2.0.1  # omit for :latest tag
# janus
JANUS_VERSION=1.3.2
LIBSRTP_VERSION=2.7.0
USRSCTP_VERSION=b28f0b55b00bde67f6be80d6623e2775b88026b8
compose.yml

static docker compose file - config is done using .env file

# compose.yml
services:
  nats:
    image: nats:${NATS_VERSION:-latest}
    #command: ["-c", "/config/gnatsd.conf"]
    command:
      - -p=4222
      #- -m 8222  # management port
      #- -D       # enable debug for logs
    restart: unless-stopped

  janus:
    image: local/janus-custom
    build:
      context: ./talk-hpb # Point to the directory containing your Dockerfile
      dockerfile: ./janus-dockerfile
    restart: unless-stopped
    networks:
      - proxy
    command:
      - janus
      - --full-trickle
      - --stun-server=coturn:3478

  signalling:
    image: strukturag/nextcloud-spreed-signaling:${SIGNALLING_VERSION:-latest}
    restart: unless-stopped
    depends_on:
      - nats
      - janus
      - coturn
    networks:
      - proxy
      - default
    env_file:
      - ./talk-hpb/signalling.env
    environment:
      - EXTERNAL_HOSTNAME=${TALKHPB_FQDN}
      - TURN_API_KEY=${COTURN_SECRET}
      - TURN_SECRET=${COTURN_SECRET}
    # Define traefik router for the signaling server
    labels:
      - traefik.enable=true
      - traefik.http.routers.signalling.rule=Host(`${TALKHPB_FQDN}`)
      - traefik.http.routers.signalling.tls=true
      - traefik.http.routers.signalling.tls.certresolver=letsencryptresolver
      - traefik.http.services.signalling.loadbalancer.server.port=8080
      - traefik.docker.network=proxy

  coturn:
    image: coturn/coturn
    container_name: coturn
    restart: unless-stopped
    ports:
      - 3478:3478
      - 3478:3478/udp
      - 50000-50099:50000-50099/udp
      - 9641:9641
    environment:
      - DETECT_EXTERNAL_IP=yes
      - DETECT_RELAY_IP=yes
    command:
      - -n
      - --log-file=/var/turn.log
      - --realm=${COTURN_FQDN}
      - --use-auth-secret
      - --static-auth-secret=${COTURN_SECRET}
      - --verbose
    volumes:
      - ./coturn/:/var/
      - ./turnserver.conf:/etc/coturn/turnserver.conf
    networks:
      - proxy
talk-hpb/signalling.env
# talk-hpb/signalling.env
# https://github.com/strukturag/nextcloud-spreed-signaling/tree/master/docker
HTTP_LISTEN=0.0.0.0:8080
TRUSTED_PROXIES=172.16.0.0/12,192.168.0.0/1,10.0.0.0/8,fc00::/7,fe80::/10,2001:db8::/32
MAX_STREAM_BITRATE=2097152 # 2Mbit/s
MAX_SCREEN_BITRATE=4194304 # 4Mbit/s
# Janus/WebRTC
USE_JANUS=1
JANUS_URL=ws://janus:8188
# No need for nats when only one Spreed-Server
NATS_URL=nats://nats:4222
TURN_SERVERS=turn:coturn:3478
#LOG_LEVEL="debug"
LOG_LEVEL="info"

#BACKENDS_ALLOWALL: Allow all backends. Extremly insecure - use only for development!
#BACKENDS_ALLOWALL_SECRET: Secret when BACKENDS_ALLOWALL is enabled.
BACKENDS="NC DEVNC TESTNC"
# nc
BACKEND_NC_URL=https://nc.mydomain.tld
BACKEND_NC_SHARED_SECRET=<<<replace me with a secret>>>
BACKEND_NC_SESSION_LIMIT=15
BACKEND_NC_MAX_STREAM_BITRATE=2097152
BACKEND_NC_MAX_SCREEN_BITRATE=4194304
# dev-nc
BACKEND_DEVNC_URL=https://dev-nc.mydomain.tld
BACKEND_DEVNC_SHARED_SECRET=<<<replace me with a secret>>>
BACKEND_DEVNC_SESSION_LIMIT=5
BACKEND_DEVNC_MAX_STREAM_BITRATE=1048576
BACKEND_DEVNC_MAX_SCREEN_BITRATE=2097152
# test-nc
BACKEND_TESTNC_URL=https://test-nc.mydomain.tld
BACKEND_TESTNC_SHARED_SECRET=<<<replace me with a secret>>>
BACKEND_TESTNC_SESSION_LIMIT=5
BACKEND_TESTNC_MAX_STREAM_BITRATE=1048576
BACKEND_TESTNC_MAX_SCREEN_BITRATE=2097152