This is an old revision of the document!
Nextcloud Talk HPB
Rootless Podman as user worker on host support. Apache + Let's Encrypt run as root. Signaling is bound to localhost only. TURN is public.
| Role | Bind | Public |
|---|---|---|
| Signaling | 127.0.0.1:8088 → container 8081 | https://talk.haacksnetworking.org (Apache) |
| TURN | 0.0.0.0:3478/tcp + 3478/udp | talk.haacksnetworking.org:3478 |
Image: ghcr.io/nextcloud-releases/aio-talk:latest
One container: signaling + NATS + Janus + eturnal.
The image rewrites /conf/signaling.conf on every start from NC_DOMAIN only. Extra Nextcloud backends must be re-added after every start. Do not mount /conf.
Do not use podman generate systemd. Units come from Quadlet files in ~/.config/containers/systemd/.
3478/tcp and 3478/udp must be reachable from clients. Signaling stays on localhost.
ufw allow 3478/tcp ufw allow 3478/udp
DNS: talk.haacksnetworking.org A/AAAA → this host.
mkdir -p ~/talk-hpb umask 077 openssl rand -hex 32 > ~/talk-hpb/signaling.secret openssl rand -hex 32 > ~/talk-hpb/turn.secret openssl rand -hex 32 > ~/talk-hpb/internal.secret
Do not rotate these after Nextcloud has them. Update every Nextcloud that uses them if you do.
Placeholders used below:
SIGNALING_SECRET=replace-with-your-signaling-secret TURN_SECRET=replace-with-your-turn-secret INTERNAL_SECRET=replace-with-your-internal-secret
File: ~/.config/containers/systemd/container-talk-hpb.container
systemctl --user disable --now container-talk-hpb.service 2>/dev/null || true rm -f ~/.config/systemd/user/container-talk-hpb.service podman rm -f talk-hpb mkdir -p ~/.config/containers/systemd cat > ~/.config/containers/systemd/container-talk-hpb.container << 'EOF' [Container] ContainerName=talk-hpb Image=ghcr.io/nextcloud-releases/aio-talk:latest PublishPort=127.0.0.1:8088:8081 PublishPort=3478:3478/tcp PublishPort=3478:3478/udp Environment=NC_DOMAIN=cloud.haacksnetworking.org Environment=TALK_HOST=talk.haacksnetworking.org Environment=TALK_PORT=3478 Environment=TZ=America/Denver Environment=TURN_SECRET=replace-with-your-turn-secret Environment=SIGNALING_SECRET=replace-with-your-signaling-secret Environment=INTERNAL_SECRET=replace-with-your-internal-secret Environment=SKIP_CERT_VERIFY=false PodmanArgs=--init --cpus=2 --memory=4g [Service] Restart=always TimeoutStopSec=120 [Install] WantedBy=default.target EOF systemctl --user daemon-reload systemctl --user reset-failed container-talk-hpb.service systemctl --user start container-talk-hpb.service sleep 25
WantedBy=default.target starts it. Do not systemctl enable a Quadlet unit. Do not podman generate systemd.
start.sh writes only backend-1 = NC_DOMAIN. For the other three hosts, patch the running file and reload signaling. Do not podman restart after the patch. A restart rewrites the file and drops the extra backends.
podman exec talk-hpb sh -c '
printf "\n[backend-2]\nurls = https://cloud.gnulinux.vip\nsecret = replace-with-your-signaling-secret\nmaxstreambitrate = 1048576\nmaxscreenbitrate = 2097152\n" >> /conf/signaling.conf
printf "\n[backend-3]\nurls = https://inside.outsidebox.club\nsecret = replace-with-your-signaling-secret\nmaxstreambitrate = 1048576\nmaxscreenbitrate = 2097152\n" >> /conf/signaling.conf
printf "\n[backend-4]\nurls = https://cloud.friend.info\nsecret = replace-with-your-signaling-secret\nmaxstreambitrate = 1048576\nmaxscreenbitrate = 2097152\n" >> /conf/signaling.conf
sed -i "s/backends = backend-1/backends = backend-1, backend-2, backend-3, backend-4/" /conf/signaling.conf
'
podman exec talk-hpb sh -c 'kill $(ps | awk "/nextcloud-spreed-signaling|[s]ignaling/{print \$1; exit}")'
podman exec talk-hpb grep -A5 '^\[backend' /conf/signaling.conf
Expected:
[backend] backends = backend-1, backend-2, backend-3, backend-4 ... [backend-1] urls = https://cloud.haacksnetworking.org ... [backend-2] urls = https://cloud.gnulinux.vip ... [backend-3] urls = https://inside.outsidebox.club ... [backend-4] urls = https://cloud.friend.info
systemctl --user is-active container-talk-hpb.service
podman inspect talk-hpb --format '{{.Name}} cpus={{.HostConfig.NanoCpus}} memory={{.HostConfig.Memory}}'
curl -sI http://127.0.0.1:8088/standalone-signaling/api/v1/welcome
Expect active, cpus=2000000000, memory=4294967296, and an HTTP response from the welcome endpoint. The image has its own HEALTHCHECK. Podman Desktop shows that status. The other containers do not, because their images do not define one.
/usr/local/bin/upgrade-high-performance.sh. A tag change is an edit to Image= in the .container file before daemon-reload. The script does not recreate the unit.
#!/bin/bash
set -euo pipefail
podman pull ghcr.io/nextcloud-releases/aio-talk:latest
systemctl --user stop container-talk-hpb.service
systemctl --user daemon-reload
systemctl --user reset-failed container-talk-hpb.service
systemctl --user start container-talk-hpb.service
sleep 25
podman exec talk-hpb sh -c '
printf "\n[backend-2]\nurls = https://cloud.gnulinux.vip\nsecret = replace-with-your-signaling-secret\nmaxstreambitrate = 1048576\nmaxscreenbitrate = 2097152\n" >> /conf/signaling.conf
printf "\n[backend-3]\nurls = https://inside.outsidebox.club\nsecret = replace-with-your-signaling-secret\nmaxstreambitrate = 1048576\nmaxscreenbitrate = 2097152\n" >> /conf/signaling.conf
printf "\n[backend-4]\nurls = https://cloud.friend.info\nsecret = replace-with-your-signaling-secret\nmaxstreambitrate = 1048576\nmaxscreenbitrate = 2097152\n" >> /conf/signaling.conf
sed -i "s/backends = backend-1/backends = backend-1, backend-2, backend-3, backend-4/" /conf/signaling.conf
'
podman exec talk-hpb sh -c 'kill $(ps | awk "/nextcloud-spreed-signaling|[s]ignaling/{print \$1; exit}")'
curl -sI http://127.0.0.1:8088/standalone-signaling/api/v1/welcome
Run it as worker:
su - worker -c '/bin/bash /usr/local/bin/upgrade-high-performance.sh'
Do not sudo -u worker. That drops the session bus.
a2enmod proxy proxy_http proxy_wstunnel headers rewrite ssl
/etc/apache2/sites-available/talk.haacksnetworking.org.conf:
<VirtualHost *:80>
ServerName talk.haacksnetworking.org
RewriteEngine On
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
<VirtualHost *:443>
ServerName talk.haacksnetworking.org
ServerAdmin support@haacksnetworking.org
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/talk.haacksnetworking.org/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/talk.haacksnetworking.org/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) ws://127.0.0.1:8088/$1 [P,L]
ProxyPass / http://127.0.0.1:8088/
ProxyPassReverse / http://127.0.0.1:8088/
ErrorLog ${APACHE_LOG_DIR}/talk.haacksnetworking.org-error.log
CustomLog ${APACHE_LOG_DIR}/talk.haacksnetworking.org-access.log combined
</VirtualHost>
a2ensite talk.haacksnetworking.org.conf apache2ctl configtest && systemctl reload apache2 curl -sI http://talk.haacksnetworking.org/ curl -sI https://talk.haacksnetworking.org/
Clients use wss://talk.haacksnetworking.org/spreed.
— oemb1905 2026/10/10 01:10