Talk connects/disconnects in a dead loop

The Basics

  • Nextcloud Server version (e.g., 29.x.x): Nextcloud Hub 26 Winter (33.0.0)
  • Operating system and version (e.g., Ubuntu 24.04): Debian 13/ Docker
  • Web server and version (e.g, Apache 2.4.25): nginx/1.29.4
  • Reverse proxy and version _(e.g. nginx 1.27.2): nginx/1.29.4
  • PHP version (e.g, 8.3): PHP 8.4.19
  • Is this the first time you’ve seen this error? (Yes / No): Yes
  • When did this problem seem to first start? First call
  • Installation method (e.g. AlO, NCP, Bare Metal/Archive, etc.)
    • Docker on private server
  • Are you using CloudfIare, mod_security, or similar? (Yes / No) No

Summary of the issue you are facing:

After a major crash, I configured a fresh instance of NC and added a HPB/Talk container to the stack (20-people calls expected). So here’s the stack I deployed:

`
version: '3.8'

services:
  # Base de données
  db:
    image: mariadb:latest
    container_name: "nextcloud2-hub-mariadb"
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    restart: always
    volumes:
      - /home/nextcloud/database:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=rootpwd
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=db-user
      - MYSQL_PASSWORD=db-password
    networks:
      - backend

  # Cache Redis (pour booster les performances)
  redis:
    image: redis:alpine
    container_name: "nextcloud2-hub-redis"
    restart: always
    networks:
      - backend

  # OnlyOffice Document Server
  onlyoffice:
    image: onlyoffice/documentserver:latest
    container_name: "nextcloud2-hub-onlyoffice"
    restart: always
    ports:
      - 8089:80
    environment:
      - JWT_ENABLED=true
      - JWT_SECRET=jwt-secret
    volumes:
      - /home/onlyoffice/data:/var/www/onlyoffice/Data
      - /home/onlyoffice/log:/var/log/onlyoffice
    networks:
      - backend

  # Antivirus ClamAV
  clamav:
    image: clamav/clamav:stable
    container_name: "nextcloud2-hub-clamav"
    restart: always
    # Le conteneur mettra un peu de temps à démarrer (mise à jour des signatures)
    volumes:
      - /home/clamav/virusdb:/var/lib/clamav/
      - /var/run/clamav/:/var/run/clamav/
    networks:
      - backend

  # Nextcloud HPB
  spreedbackend:
    image: ghcr.io/nextcloud-releases/aio-talk:latest
    container_name: "nextcloud2-hub-talk"
    init: true
    ports:
      - 3478:3478/tcp
      - 3478:3478/udp
      - 8181:8081/tcp
    environment:
      - NC_DOMAIN=nc.mydomain.com
      - TALK_HOST=nc.mydomain.com
      - TURN_SECRET=turn-secret
      - SIGNALING_SECRET=sig-secret
      - INTERNAL_SECRET=int-secret
      - TZ=Europe/Brussels
      - TALK_PORT=3478
    restart: unless-stopped   
    networks:
      - frontend
      - backend
    
  # Serveur de fichiers Nextcloud
  app:
    image: nextcloud:latest
    container_name: "nextcloud2-hub-app"
    restart: always
    ports:
      - 8080:80
    depends_on:
      - db
      - redis
    volumes:
      - /home/nextcloud/html:/var/www/html
      - /home/nextcloud/data:/var/www/html/data
      - /home/clamav/virusdb:/var/lib/clamav/
      - /var/run/clamav/:/var/run/clamav/
    environment:
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=db-user
      - MYSQL_PASSWORD=db-password
      - MYSQL_HOST=db
      - REDIS_HOST=redis
    networks:
      - frontend
      - backend

# Définition des volumes
volumes:
  nextcloud_data:
  nextcloud_db:
  onlyoffice_data:
  onlyoffice_log:

# Définition des réseaux
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

I also updated the NC/Talk settings to use HPB URL = https://nc.mydomain.com/standalone-signaling / sig-secret which triggered the green checkmark (OK : version actuelle : 2.1.0~docker).

Steps to replicate it (hint: details matter!):

  1. Open Talk and create a conversation.

  2. Invite another participant.

  3. Caller gets the camera/mic settings OK and the call is initiated. Their camera correctly displays in the browser, mic seems OK too. They can hear the ringtone.

  4. The person being called cannot receive nor answer the call and the connection “blinks”: call is cancelled, then initiated again, then cancelled, etc. in a dead loop.

  5. The caller sometimes gets an error message saying the TURN server couldn’t handle the call (don’t remember the exact message as it vanishes quite fast).

  6. The caller should leave and delete the conversation to end the loop.

The browser console doesn’t provide more information, but maybe I just don’t know what I should look for…

Thanks in advance for any help!

Log entries

Nextcloud

Please provide the log entries from your Nextcloud log that are generated during the time of problem (via the Copy raw option from Administration settings->Logging screen or from your nextcloud.log located in your data directory). Feel free to use a pastebin/gist service if necessary.

[core] Erreur: Could not decrypt or decode encrypted session data
GET /an.php
de 74.235.238.88 par – à 26 mars 2026, 15:32:26

[core] Erreur: Could not decrypt or decode encrypted session data
GET /class-t.api.php
de 20.63.96.180 par – à 26 mars 2026, 16:04:32

Web server / Reverse Proxy

The output of your Apache/nginx/system log in /var/log/____: too large, on demand if needed

Configuration

Nextcloud

The output of occ config:list system or similar is best, but, if not possible, the contents of your config.php file from /path/to/nextcloud is fine (make sure to remove any identifiable information!):

{
    "system": {
        "serverid": 1,
        "htaccess.RewriteBase": "\/",
        "memcache.local": "\\OC\\Memcache\\APCu",
        "apps_paths": [
            {
                "path": "\/var\/www\/html\/apps",
                "url": "\/apps",
                "writable": false
            },
            {
                "path": "\/var\/www\/html\/custom_apps",
                "url": "\/custom_apps",
                "writable": true
            }
        ],
        "memcache.distributed": "\\OC\\Memcache\\Redis",
        "memcache.locking": "\\OC\\Memcache\\Redis",
        "redis": {
            "host": "***REMOVED SENSITIVE VALUE***",
            "password": "***REMOVED SENSITIVE VALUE***",
            "port": 6379
        },
        "upgrade.disable-web": true,
        "instanceid": "***REMOVED SENSITIVE VALUE***",
        "passwordsalt": "***REMOVED SENSITIVE VALUE***",
        "secret": "***REMOVED SENSITIVE VALUE***",
        "trusted_domains": [
            "nc.mydomain.com"
        ],
        "datadirectory": "***REMOVED SENSITIVE VALUE***",
        "dbtype": "mysql",
        "version": "33.0.0.16",
        "overwrite.cli.url": "http:\/\/files.eurosmart.com",
        "dbname": "***REMOVED SENSITIVE VALUE***",
        "dbhost": "***REMOVED SENSITIVE VALUE***",
        "dbtableprefix": "oc_",
        "mysql.utf8mb4": true,
        "dbuser": "***REMOVED SENSITIVE VALUE***",
        "dbpassword": "***REMOVED SENSITIVE VALUE***",
        "installed": true,
        "mail_smtppassword": "***REMOVED SENSITIVE VALUE***",
        "mail_smtpname": "***REMOVED SENSITIVE VALUE***",
        "mail_domain": "***REMOVED SENSITIVE VALUE***",
        "mail_from_address": "***REMOVED SENSITIVE VALUE***",
        "mail_smtpmode": "smtp",
        "mail_smtpsecure": "ssl",
        "mail_smtphost": "***REMOVED SENSITIVE VALUE***",
        "mail_smtpauth": true,
        "mail_smtpport": "465",
        "mail_sendmailmode": "smtp",
        "mail_smtpstreamoptions": {
            "ssl": {
                "allow_self_signed": false,
                "verify_peer": true,
                "verify_peer_name": true
            }
        },
        "defaultapp": "dashboard",
        "default_phone_region": "BE",
        "skeletondirectory": "",
        "maintenance_window_start": 0,
        "maintenance": false,
        "trusted_proxies": "***REMOVED SENSITIVE VALUE***",
        "overwritehost": "nc.mydomain.com",
        "overwriteprotocol": "https",
        "twofactor_enforced": "true",
        "twofactor_enforced_groups": [
            "admin"
        ],
        "twofactor_enforced_excluded_groups": [
            "Board",
            "CDI",
            "Cloud",
            "GA",
            "ISCI",
            "ITSC",
            "IoT"
        ],
        "data-fingerprint": "3d8f5f2d45150ece883268bdd8c22688"
    }
}

Apps

The output of occ app:list (if possible).

Enabled:
activity: 6.0.0-dev.0
app_api: 33.0.0
bruteforcesettings: 6.0.0-dev.0
calendar: 6.2.1
circles: 33.0.0
cloud_federation_api: 1.17.0
comments: 1.23.0
contactsinteraction: 1.14.1
dashboard: 7.13.0
dav: 1.36.0
federatedfilesharing: 1.23.0
federation: 1.23.0
files: 2.5.0
files_antivirus: 6.2.0
files_downloadlimit: 5.1.0-dev.0
files_pdfviewer: 6.0.0-dev.0
files_reminders: 1.6.0
files_sharing: 1.25.2
files_trashbin: 1.23.0
files_versions: 1.26.0
firstrunwizard: 6.0.0-dev.0
logreader: 6.0.0
lookup_server_connector: 1.21.0
nextcloud_announcements: 5.0.0
notes: 4.13.1
notifications: 6.0.0
notify_push: 1.3.1
oauth2: 1.21.0
onlyoffice: 10.0.0
password_policy: 5.0.0-dev.0
photos: 6.0.0-dev.0
polls: 8.6.3
privacy: 5.0.0-dev.0
profile: 1.2.0
provisioning_api: 1.23.0
quota_warning: 1.23.0
recommendations: 6.0.0-dev.0
related_resources: 4.0.0-dev.0
serverinfo: 5.0.0-dev.0
settings: 1.16.0
sharebymail: 1.23.0
spreed: 23.0.2
support: 5.0.0
survey_client: 5.0.0-dev.0
systemtags: 1.23.0
text: 7.0.0-dev.3
theming: 2.8.0
twofactor_backupcodes: 1.22.0
twofactor_totp: 15.0.0-dev.0
updatenotification: 1.23.0
user_status: 1.13.0
viewer: 6.0.0-dev.0
weather_status: 1.13.0
webhook_listeners: 1.5.0
workflowengine: 2.15.0

Disabled:
admin_audit: 1.23.0
encryption: 2.21.0
files_external: 1.25.1
richdocumentscode: 25.4.904 (installed 25.4.904)
suspicious_login: 11.0.0-dev.0
twofactor_nextcloud_notification: 7.0.0
user_ldap: 1.24.0

More details: the server is running an nginx proxy server before the NC containers. Here’s the configuration file I’m currently using.

#
# After any modification : sudo systemctl restart nginx
#

# For Nextcloud Talk (Spreed)
upstream signaling {
    server 127.0.0.1:8181;
}

server {
  listen 80;
  listen [::]:80;            # comment to disable IPv6
  
  server_name files.eurosmart.com;

  if ($scheme = "http") {
      return 301 https://$host$request_uri;
  }
  if ($http_x_forwarded_proto = "http") {
      return 301 https://$host$request_uri;
  }
}

server {
  listen 443 ssl;      # for nginx v1.25.1+
  listen [::]:443 ssl; # for nginx v1.25.1+ - keep comment to disable IPv6
  http2 on;            # uncomment to enable HTTP/2 - supported on nginx v1.25.1+
  
  server_name files.eurosmart.com;

  # SSL configuration
  ssl_certificate /etc/letsencrypt/live/nc.mydomain.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/nc.mydomain.com/privkey.pem;

  ssl_dhparam /etc/dhparam; # curl -L https://ssl-config.mozilla.org/ffdhe2048.txt -o /etc/dhparam

  ssl_early_data on;
  ssl_session_timeout 1d;
  ssl_session_cache shared:SSL:10m;

  ssl_protocols TLSv1.2 TLSv1.3;
  # ssl_ecdh_curve x25519:x448:secp521r1:secp384r1:secp256r1;

  ssl_prefer_server_ciphers on;
  ssl_conf_command Options PrioritizeChaCha;
  ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256;
  
  add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload" always;
  
  # Nextcloud Hub
  location / {
    proxy_pass http://localhost:8088;

    # proxy_set_header Host $http_host;

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_set_header X-Forwarded-Scheme $scheme;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header Early-Data $ssl_early_data;

    # WebSocket support for Talk
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Sec-WebSocket-Protocol $http_sec_websocket_protocol;
    proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions;
    proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;

    proxy_buffering off;
    proxy_request_buffering off;

    client_max_body_size 100M;
    client_body_buffer_size 512k;
    proxy_read_timeout 86400s;

    # Enable gzip but do not remove ETag headers
    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
  }

  # Make a regex exception for `/.well-known` so that clients can still
  # access it despite the existence of the regex rule
  # `location ~ /(\.|autotest|...)` which would otherwise handle requests
  # for `/.well-known`.
  location ^~ /.well-known {
    # The rules in this block are an adaptation of the rules
    # in `.htaccess` that concern `/.well-known`.

    location = /.well-known/carddav { return 301 /remote.php/dav/; }
    location = /.well-known/caldav  { return 301 /remote.php/dav/; }

    location /.well-known/acme-challenge    { try_files $uri $uri/ =404; }
    location /.well-known/pki-validation    { try_files $uri $uri/ =404; }

    # Let Nextcloud's API for `/.well-known` URIs handle all other
    # requests by passing them to the front-end controller.
    return 301 /index.php$request_uri;
  }    

  # OnlyOffice
  location /ooserver/ {
    proxy_pass http://localhost:8089/;
    proxy_redirect     off;

    client_max_body_size 100M;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    proxy_set_header Host $http_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-Host $host/ooserver;
    proxy_set_header X-Forwarded-Proto $scheme;
  }

  # WebRTC STUN/TURN (if using standalone signaling)
  location /standalone-signaling/ {
    proxy_pass http://signaling/;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
    # WebSocket support
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }

  # Nextcloud Talk (Spreed) WebSocket & Signaling
  location /spreed {
    proxy_pass http://signaling/spreed;
    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;

    # WebSocket support
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    # Disable buffering for real-time media
    proxy_buffering off;
    proxy_cache_bypass $http_upgrade;
  }

  # For crawler robots
  location = /robots.txt {
      allow all;
      log_not_found off;
      access_log off;
  }

  # Rules borrowed from `.htaccess` to hide certain paths from clients
  location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  { return 404; }
  location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)                { return 404; }
  
}

I’m not an Nginx expert and wonder whether the sites /standalone-signaling/ and /spreed would not hide some misconfiguration.

Can anyone help on this point? How can I ensure the HPS is accessible from NC?

Thanks in advance for any help!