Setup Signaling server (High-performance backend) for Nextcloud Talk

For a small setup, you can simply use a TURN server to make calls, and it will suffice. However, for large installations with many users, it is recommended to set up a separate Signaling server to reduce the load on participants joining the call. In this guide, I will walk you through the complete setup process of installing a Signaling server (High-performance backend) for your Nextcloud Talk.

What you will need:

  • Hardware is dependent on your needs, start with 4GB RAM and 2 vCPU
  • Fresh Ubuntu 22.04 LTS server with public IP
  • A domain or sub-domain pointed to the server IP
  • Knowledge of running Linux commands

For clarity, I will use as an example domain throughout this guide.

So let’s get started..


1- Prepare the system

If you haven’t updated your system yet, go ahead and do so with apt update && apt upgrade.

Next is to enable the firewall and open a few ports. For details, check out this article.


  • 22 (your SSH port, if different use that)
  • 80 (TCP)
  • 443 (TCP)
  • 3478 (UDP and TCP)

Port 3478 depends on your needs. If you are going to use this server as a TURN server too, then open it; otherwise, you can skip it, as we will use TURN locally on this signaling server only.

After completing the above steps, proceed to install the following packages.

apt install make protobuf-compiler git python3 zip unzip

2- Install latest Go

The signaling server will not compile with the current Go language provided by Ubuntu in the default repository. For this, we will need at least version 1.20 or above. Follow this guide to download and set up Go 1.20.

3- Setup NATS server

NATS is used to distribute messages between different instances. They provide a Docker solution, which we will use here.

docker pull nats:latest

Let’s run it in the background.

docker run -d --name nats-server -p 4222:4222 -ti nats:latest

You can check the status with docker ps. The port we will need later in the setup is 4222.

4- Install Janus (WebRTC gateway)

Janus is available in the Ubuntu repositories, and we will install it from there.

apt install janus

systemctl enable janus
systemctl status janus

We need to configure a few things in Janus first. You can create a random 16-character key by yourself or by using OpenSSL.

openssl rand -hex 8

Open this file for editing.

vim /etc/janus/janus.jcfg

Look for full_trickle under the nat section, which should be around line number 274.

full_trickle = true
turn_rest_api_key = <key_created_above>

Change the interface to lo in /etc/janus/janus.transport.http.jcfg. Additionally, in /etc/janus/janus.transport.websockets.jcfg, update ws_interface to ‘lo‘.

After all these changes, restart Janus.

systemctl restart janus

5- Install coTURN

apt install coturn
systemctl enable coturn

cp /etc/turnserver.conf /etc/turnserver.conf_BAK

Create a random key with:

openssl rand -hex 16

Open the /etc/turnserver.conf file for editing and add the following lines (remove everything else) to it, making the necessary changes according to your requirements.


Save and restart coTURN.

systemctl restart coturn

6- Download and compile Signaling server

We will now download and prepare the Signaling server. While you can download the latest build from the main branch via git clone, I advise against it for stability reasons. Instead, we will download the most recent stable version, which is 1.2.2 at the time of writing this.

cd /opt


It’s time to build the binary for the Signaling server.

cd nextcloud-spreed-signaling-1.2.2
make build

If everything goes smoothly, you will find the binary inside the bin directory. Let’s copy it to /usr/bin.

cp bin/signaling /usr/bin/

We will also copy the file to the /etc/signaling location as server.conf.

mkdir /etc/signaling

cp /etc/signaling/server.conf

7- Prepare Signaling server

We will now add a system user with no login that will run the Signaling server.

groupadd --system signaling

useradd --system \
    --gid signaling \
    --shell /usr/sbin/nologin \
    --comment "Standalone signaling server for Nextcloud Talk." \

Afterward, change the permissions of server.conf.

chmod 600 /etc/signaling/server.conf

chown signaling:signaling /etc/signaling/server.conf

To run the Signaling server as a daemon, copy the systemd file provided by Signaling to the systemd directory.

cp /opt/nextcloud-spreed-signaling-1.2.2/dist/init/systemd/signaling.service /etc/systemd/system/signaling.service

Next, we will edit it and add the following line after the Description line.

vim /etc/systemd/system/signaling.service

You can now reload the systemd daemon.

systemctl daemon-reload

8- Configure Signaling server

Before we proceed to start the Signaling server, we will modify the configuration file to fit our needs.

At this point we will need four random keys.

Hash key:

openssl rand -hex 16

Block key:

openssl rand -hex 16

Internal secret (used for external clients like recording backend server):

openssl rand -hex 16

Nextcloud secret key:

openssl rand -hex 16

Note down the keys and from step 4 and 5 above.

vim /etc/signaling/server.conf

Do the following in this file:

  • Uncomment where needed
  • Change where necessary like keys etc
  • You can leave nats, janus and turn server urls as is
  • Double check everything before saving it

Note: I have added line numbers in front of them so you can easily locate them.

listen = (line 4)

hashkey = <hashkey_created_above> (line 40)
blockkey = <blockkey_created_above> (line 45)

internalsecret = <clientkey_created_above> (line 50)

backends = backend-1 (line 67)

[backend-1] (line 109)
url = https://your.nextcloud.domain (line 111)
secret = <nextcloud_secret_key_created_above>  (line 116)

url = nats://localhost:4222 (line 144)

type = janus (line 149)
url = ws:// (line 154)

apikey = <turn_rest_api_key_set_in_janus_step_4_above> (line 209)
secret = <turnserver_secret_created_in_turnserver.conf_as_static-auth-secret_step_5_above>  (line 209)
servers = turn:,turn: (line 209)

Save the configuration file and proceed to start the Signaling server.

systemctl enable signaling
systemctl start signaling

Check it’s status with:

systemctl status signaling

Your output will be similar to the one below.

● signaling.service - Nextcloud Talk signaling server
     Loaded: loaded (/etc/systemd/system/signaling.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2024-01-05 18:46:59 UTC; 13s ago
   Main PID: 669700 (signaling)
      Tasks: 6 (limit: 2237)
     Memory: 3.1M
        CPU: 98ms
     CGroup: /system.slice/signaling.service
             └─669700 /usr/bin/signaling --config /etc/signaling/server.conf

Jan 05 18:47:00 vps2617142 signaling[669700]: mcu_janus.go:316: Created Janus session 3896517509338209
Jan 05 18:47:00 vps2617142 signaling[669700]: mcu_janus.go:323: Created Janus handle 8168179460168699
Jan 05 18:47:00 vps2617142 signaling[669700]: main.go:263: Using janus MCU
Jan 05 18:47:00 vps2617142 signaling[669700]: hub.go:387: Using a timeout of 10s for MCU requests
Jan 05 18:47:00 vps2617142 signaling[669700]: backend_server.go:98: Using configured TURN API key
Jan 05 18:47:00 vps2617142 signaling[669700]: backend_server.go:99: Using configured shared TURN secret
Jan 05 18:47:00 vps2617142 signaling[669700]: backend_server.go:101: Adding "turn:" as TURN serv>
Jan 05 18:47:00 vps2617142 signaling[669700]: backend_server.go:101: Adding "turn:" as TURN serv>
Jan 05 18:47:00 vps2617142 signaling[669700]: backend_server.go:114: No IPs configured for the stats endpoint, only allowing>
Jan 05 18:47:00 vps2617142 signaling[669700]: main.go:339: Listening on

9- Proxy server

Your choice of frontend or proxy depends on your ease of use. I will use Apache. You can check the other proxies here.

apt install apache2

a2enmod ssl rewrite headers proxy proxy_http deflate cache proxy_wstunnel http2 proxy_fcgi env expires

systemctl restart apache2
systemctl enable apache2

rm /var/www/html/index.html

We will proceed to create a virtual host file.

cd /etc/apache2/sites-available/

vim signaling.conf

Add the following to it:

<VirtualHost *:80>

	DocumentRoot /var/www/html

	<Directory "/var/www/html">
		AllowOverride All
		Options -Indexes +FollowSymLinks

	ErrorLog /var/log/apache2/signaling_error.log

Enable this configuration and restart Apache.

a2dissite 000-default.conf
a2ensite signaling.conf

apachectl -t
systemctl restart apache2

10- SSL

If you have your own certificates, you can use them in the configuration file below (step 11). But for everyone else, we will install Certbot to obtain a free SSL certificate from Let’s Encrypt.

apt install certbot

Obtain a free certificate and make a note of their paths.

certbot certonly --webroot -w /var/www/html -d

To renew the certificate automatically, you can set up a cronjob.

crontab -e

Add the following to it.

30 04 * * * certbot renew

11- Update virtual host

We can now safely update the virtual host file with the 443 block section.

Add the following to it:

<VirtualHost *:443>

	SSLProtocol all -SSLv2 -SSLv3
	SSLHonorCipherOrder on
	SSLCompression off

	ErrorLog /var/log/apache2/signaling_error.log

	SSLEngine on
	SSLCertificateKeyFile /etc/letsencrypt/live/
	SSLCertificateFile /etc/letsencrypt/live/

	ProxyPass "/standalone-signaling/"  "ws://"

    RewriteEngine On
    RewriteRule ^/standalone-signaling/spreed/$ - [L]
    RewriteRule ^/standalone-signaling/api/(.*)$1 [L,P]

Save this and restart Apache.

apachectl -t
systemctl restart apache2

12- Test and add it to Nextcloud

Once everything is set up and configured, you can now give it a shot to test.

curl -i

The output will look like the one below; ignore the version as unknown. I don’t know why it is not showing the exact version of Signaling.

HTTP/2 200 
content-type: application/json; charset=utf-8
server: nextcloud-spreed-signaling/unknown
x-spreed-signaling-features: audio-video-permissions, dialout, hello-v2, incall-all, mcu, simulcast, switchto, transient-data, update-sdp, welcome
date: Mon, 08 Jan 2024 16:48:17 GMT
content-length: 61


Now, navigate to the Nextcloud administration settings page, click on Talk, scroll to the High-performance backend section, and then click on the “Add a new high-performance backend server” button.

Enter the following URL in the URL field and enter your Nextcloud secret key, created above, in the Shared secret field. You can check the Validate SSL certificate checkbox.


That’s it—happy calling! 📞

13- Debugging

Once you initiate the call and encounter any issues or if something is not working, you can check the logs to further debug the issue.

tail -f /var/log/syslog

tail -f /var/log/janus.log

tail -f /var/log/apache2/signaling_error.log

docker logs --since=1h <container_name_or_id>

Note: Janus logs are disabled by default; you can enable them in /etc/janus/janus.jcfg by providing the path as shown above.


Are you looking to set up Talk Recording Backend? Click here

One comment

  1. websocket: the client is not using the websocket protocol: ‘upgrade’ token not found in ‘Connection’ header

Leave a Reply

Your email address will not be published. Required fields are marked *