This is an old revision of the document!
# Nextcloud Talk HPB (aio-talk) — Rootless Podman Quadlet
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/`.
—
## 0. Host firewall (root)
`3478/tcp` and `3478/udp` must be reachable from clients. Signaling stays on localhost.
```bash ufw allow 3478/tcp ufw allow 3478/udp ```
DNS: `talk.haacksnetworking.org` A/AAAA → this host.
—
## 1. Directories and secrets (worker)
```bash 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 ```
—
## 2. Quadlet
File: `~/.config/containers/systemd/container-talk-hpb.container`
```bash 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`.
—
## 3. Extra Nextcloud backends (after every start)
`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.
```bash 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 ```
—
## 4. Verify
```bash 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.
—
## 5. Upgrade script
`/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.
```bash #!/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`:
```bash su - worker -c '/bin/bash /usr/local/bin/upgrade-high-performance.sh' ```
Do not `sudo -u worker`. That drops the session bus.
—
## 6. Apache reverse proxy (root)
```bash a2enmod proxy proxy_http proxy_wstunnel headers rewrite ssl ```
`/etc/apache2/sites-available/talk.haacksnetworking.org.conf`:
```apache <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> ```
```bash 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`.
—