I have been trying to setup my multi-domain Nexcloud installation (which has been running for more than a decade) together with the High Performance Backend service for Talk conversations. After many attemps, I managed to make it work by creating my own docker-compose stack with 3 services (nats, janus and spreed-signaling), and avoid using Package aio-talk · GitHub
This guide provides a complete setup for deploying a Nextcloud Talk High Performance Backend (HPB) server that supports multiple Nextcloud domains using a single HPB instance. I hope it is useful for anyone dealing with this issue.
Architecture
The setup consists of three main components running in Docker containers:
- NATS: Message broker for signaling
- Janus Gateway: WebRTC media server
- Nextcloud Spreed Signaling: Signaling server that connects Nextcloud to Janus
All components use network_mode: host for optimal WebRTC performance and to avoid port mapping issues with large UDP port ranges.
Step 1: Directory Structure
Create the configuration directory structure:
sudo mkdir -p /opt/hpb/config/janus
cd /opt/hpb
Step 2: Generate Secrets
Generate secure random secrets for your configuration:
# TURN secret (64 characters / 32 bytes)
openssl rand -hex 32
# Signaling secret (64 characters / 32 bytes)
openssl rand -hex 32
# Hash key (64 characters / 32 bytes)
openssl rand -hex 32
# Block key - IMPORTANT: Must be exactly 16 bytes (32 hex chars)
openssl rand -hex 16
# Internal secret (64 characters / 32 bytes)
openssl rand -hex 32
CRITICAL: The blockkey must be exactly 16, 24, or 32 bytes (32, 48, or 64 hex characters). Using 16 bytes (32 characters) is recommended. If you use a longer key, the signaling server will fail to start with an error about key length.
Step 3: NATS Configuration
Create /opt/hpb/config/nats.conf:
listen: 127.0.0.1:4222
Step 4: Janus Gateway Configuration
Main Janus Configuration
Create /opt/hpb/config/janus/janus.jcfg:
general: {
configs_folder = "/usr/local/etc/janus"
plugins_folder = "/usr/local/lib/janus/plugins"
transports_folder = "/usr/local/lib/janus/transports"
events_folder = "/usr/local/lib/janus/events"
loggers_folder = "/usr/local/lib/janus/loggers"
debug_level = 4
log_to_stdout = true
}
nat: {
ice_lite = false
ice_tcp = false
full_trickle = true
rtp_port_range = "20000-40000"
}
media: {
ipv6 = false
}
plugins: {
}
transports: {
}
WebSocket Transport Configuration
Create /opt/hpb/config/janus/janus.transport.websockets.jcfg:
general: {
}
admin: {
admin_ws = true
admin_ws_port = 7188
}
ws: {
ws = true
ws_port = 8188
ws_interface = "127.0.0.1"
}
wss: {
wss = false
}
VideoRoom Plugin Configuration
Create /opt/hpb/config/janus/janus.plugin.videoroom.jcfg:
general: {
admin_key = "your_admin_key_here"
}
room-1234: {
description = "Demo Room"
secret = "room_admin_password"
publishers = 6
bitrate = 128000
fir_freq = 10
audiocodec = "opus"
videocodec = "vp8"
record = false
}
Step 5: Signaling Server Configuration
Create /opt/hpb/config/server.conf:
[http]
listen = 0.0.0.0:8081
[app]
# Shared secret - must match what you configure in Nextcloud Talk settings
secret = YOUR_SIGNALING_SECRET_HERE
[sessions]
# Hash key: 64 hex characters (32 bytes)
hashkey = YOUR_HASH_KEY_HERE
# Block key: MUST be exactly 32 hex characters (16 bytes), 48 (24 bytes), or 64 (32 bytes)
blockkey = YOUR_BLOCK_KEY_HERE
[nats]
url = nats://127.0.0.1:4222
[mcu]
type = janus
url = ws://127.0.0.1:8188
[backend]
# List all your Nextcloud backends here
backends = backend1, backend2, backend3
# Backend 1
[backend1]
url = https://cloud.example.com
# This secret must match the signaling secret above
secret = YOUR_SIGNALING_SECRET_HERE
# Backend 2
[backend2]
url = https://files.example.org
secret = YOUR_SIGNALING_SECRET_HERE
# Backend 3
[backend3]
url = https://nextcloud.another-domain.net
secret = YOUR_SIGNALING_SECRET_HERE
# TURN server configuration
[turn]
# Use "api" not "apikey"
api = static
secret = YOUR_TURN_SECRET_HERE
servers = turn:hpb.example.com:3478?transport=udp,turn:hpb.example.com:3478?transport=tcp
Important Notes:
- All backends must use the same signaling secret
- The
apiparameter should bestatic, notapikey - Replace
YOUR_*_SECRET_HEREwith the secrets you generated in Step 2 - Replace domain names with your actual domains
Step 6: Docker Compose Configuration
Create /opt/hpb/docker-compose.yml (or wherever you prefer to store it):
ame: 'hpb'
services:
nats:
container_name: nats_server
image: nats:latest
command: ["-c", "/config/nats.conf"]
volumes:
- /opt/hpb/config/nats.conf:/config/nats.conf:ro
network_mode: host
restart: unless-stopped
janus:
container_name: janus_gateway
image: canyan/janus-gateway:latest
network_mode: host
environment:
- JANUS_API_HTTP=yes
- JANUS_API_HTTPS=no
- JANUS_API_WS=yes
- JANUS_API_ADMIN_WS=yes
- JANUS_RTP_PORT_RANGE=20000-40000
restart: unless-stopped
signaling:
container_name: spreed_signaling
image: strukturag/nextcloud-spreed-signaling:latest
depends_on:
- nats
- janus
network_mode: host
volumes:
- /opt/hpb/config/server.conf:/config/server.conf:ro
# command: ["-config", "/config/server.conf"]
environment:
- CONFIG_FILE=/etc/signaling/server.conf
restart: unless-stopped
Step 7: Firewall Configuration
Allow the necessary ports through your firewall:
# TURN/STUN
sudo ufw allow 3478/tcp
sudo ufw allow 3478/udp
# Signaling (if accessing directly)
sudo ufw allow 8081/tcp
# RTP for WebRTC
sudo ufw allow 20000:40000/udp
# Verify rules
sudo ufw status numbered
Step 8: Nginx Reverse Proxy (Recommended)
For production use, set up a reverse proxy with SSL termination:
Create /etc/nginx/sites-available/hpb:
server {
listen 80;
server_name hpb.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name hpb.example.com;
ssl_certificate /etc/letsencrypt/live/hpb.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hpb.example.com/privkey.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location /standalone-signaling/ {
proxy_pass http://127.0.0.1:8081/;
proxy_http_version 1.1;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Long-lived connections for WebSocket
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
}
}
Enable the site:
sudo ln -s /etc/nginx/sites-available/hpb /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 9: Start the Services
cd /opt/hpb # or wherever your docker-compose.yml is located
docker compose up -d
Step 10: Verify Installation
Check that all services are running:
# View running containers
docker ps
# Check NATS logs
docker logs nats_server
# Check Janus logs
docker logs janus_gateway
# Check Signaling logs (should show all backends loaded)
docker logs spreed_signaling
In the signaling logs, you should see:
Backend backend1 added for https://cloud.example.com/
Backend backend2 added for https://files.example.org/
Backend backend3 added for https://nextcloud.another-domain.net/
...
Listening on 0.0.0.0:8081
Test the endpoint:
curl http://localhost:8081/api/v1/welcome
# Should return: {"version":"X.X.X"}
# Or with SSL via Nginx:
curl https://hpb.example.com/standalone-signaling/api/v1/welcome
Step 11: Configure Nextcloud Talk
On each of your Nextcloud instances:
-
Go to Administration → Talk → High-performance backend
-
Configure the following (identical on all instances):
- Signaling server URL:
https://hpb.example.com/standalone-signaling - Shared secret: Use the same
YOUR_SIGNALING_SECRET_HEREfrom your server.conf - Verify SSL certificate: ✓ Enabled
- Signaling server URL:
-
Configure TURN servers:
- TURN server:
hpb.example.com:3478 - Protocols: UDP and TCP
- Secret: Use the
YOUR_TURN_SECRET_HEREfrom your server.conf
- TURN server:
-
You should see: ✓ OK: Version in use: X.X.X~docker
Adding More Domains
To add additional Nextcloud instances:
- Edit
/opt/hpb/config/server.conf - Add the new backend to the backends list and create its section:
[backend]
backends = backend1, backend2, backend3, backend4
[backend4]
url = https://new-domain.example.com
secret = YOUR_SIGNALING_SECRET_HERE
- Restart the signaling service:
docker compose restart signaling
- Configure Talk on the new Nextcloud instance