Skip to content

Instantly share code, notes, and snippets.

@pojntfx
Last active January 27, 2026 09:18
Show Gist options
  • Select an option

  • Save pojntfx/151d3fa0c76ada3a79cfa2b5dcf6e2f6 to your computer and use it in GitHub Desktop.

Select an option

Save pojntfx/151d3fa0c76ada3a79cfa2b5dcf6e2f6 to your computer and use it in GitHub Desktop.
Setup High-Performance Nextcloud Talk Backend and Recording Server on Hetzner

Nextcloud High Performance Backend and Recording Setup

Commercial hosted providers (I recommend using these instead of hosting it yourself, esp. if you're setting this up for someone else):

This pad is heavily based on https://arnowelzel.de/en/nextcloud-talk-high-performance-backend-with-docker - it's probably best to check if there were any changes in that article before you proceede here.

High-Performance Backend

ssh root@<HPB_IPV4_ADDRESS>
ssh root@<HPB_IPV6_ADDRESS>
ssh root@<HPB_HOSTNAME>
[root@nextcloud-hbp ~]# openssl rand --hex 16
<TURN_SECRET> # TURN_SECRET
[root@nextcloud-hbp ~]# openssl rand --hex 16
<SIGNALING_SECRET> # SIGNALING_SECRET
[root@nextcloud-hbp ~]# openssl rand --hex 16
<INTERNAL_SECRET> # INTERNAL_SECRET
sudo dnf install -y podman podman-compose
sudo podman run -t -d --name="nextcloud-hpb" \
  -p 3478:3478 \
  -p 3478:3478/udp \
  -p 8090:8081 \
  -e "NC_DOMAIN=<NEXTCLOUD_DOMAIN>" \
  -e "TALK_HOST=<HPB_HOSTNAME>" \
  -e "TURN_SECRET=<TURN_SECRET>" \
  -e "SIGNALING_SECRET=<SIGNALING_SECRET>" \
  -e "TZ=Europe/Berlin" \
  -e "TALK_PORT=3478" \
  -e "INTERNAL_SECRET=<INTERNAL_SECRET>" \
  --restart always \
  ghcr.io/nextcloud-releases/aio-talk:latest
mkdir -p ~/caddy-hpb
cat > ~/caddy-hpb/Caddyfile << 'EOF'
<HPB_HOSTNAME> {
    # WebSocket endpoint for signaling
    @websocket {
        path /standalone-signaling
        header Connection *Upgrade*
        header Upgrade websocket
    }
    reverse_proxy @websocket 127.0.0.1:8090

    # API endpoints
    handle /standalone-signaling/api/* {
        reverse_proxy 127.0.0.1:8090
    }

    # Spreed endpoint
    handle /standalone-signaling/spreed/* {
        reverse_proxy 127.0.0.1:8090
    }

    # Default handler for other standalone-signaling paths
    handle /standalone-signaling* {
        reverse_proxy 127.0.0.1:8090
    }
}
EOF
cat > ~/caddy-hpb/Caddyfile << 'EOF'
<HPB_HOSTNAME> {
    handle /standalone-signaling/* {
        uri strip_prefix /standalone-signaling
        reverse_proxy 127.0.0.1:8090
    }

    handle /standalone-signaling {
        uri strip_prefix /standalone-signaling
        reverse_proxy 127.0.0.1:8090
    }
}
EOF
sudo podman run -d --name="caddy-hpb" \
  --network=host \
  -v ~/caddy-hpb/Caddyfile:/etc/caddy/Caddyfile:Z \
  -v caddy_data:/data \
  -v caddy_config:/config \
  --restart always \
  docker.io/library/caddy:latest
curl https://<HPB_HOSTNAME>/standalone-signaling/api/v1/welcome
occ config:system:set overwrite.cli.url --type=string --value https://<NEXTCLOUD_DOMAIN> # For custom domain, via https://konsoleh.hetzner.com/storage_share_occ.php, else you might get issues with CORS

Now, go to https://<NEXTCLOUD_DOMAIN>/settings/admin/talk

Remove:

stun.your-storageshare.de:443
turn.your-storageshare.de:443 <EXISTING_TURN_SECRET>

Add:

High-performance backend URL: https://<HPB_HOSTNAME>/standalone-signaling
Shared secret: SIGNALING_SECRET from above
STUN server: <HPB_HOSTNAME>:3478
TURN server URL: <HPB_HOSTNAME>:3478
STUN server secret: TURN_SECRET from above

Recording Backend

This needs the HPB to be set up

ssh root@<RECORDING_IPV4_ADDRESS>
ssh root@<RECORDING_IPV6_ADDRESS>
ssh root@<RECORDING_HOSTNAME>
[root@nextcloud-recording ~]# openssl rand --hex 16
<RECORDING_SECRET> # RECORDING_SECRET
sudo dnf install -y podman podman-compose
sudo podman run -d --name "nextcloud-recording" \
  -e HPB_DOMAIN=<HPB_HOSTNAME> \
  -e NC_DOMAIN=<NEXTCLOUD_DOMAIN> \
  -e RECORDING_SECRET=<RECORDING_SECRET> \
  -e INTERNAL_SECRET=<INTERNAL_SECRET> \
  -e TZ=Europe/Berlin \
  -p 127.0.0.1:1234:1234 \
  --restart=always \
  nextcloud/aio-talk-recording:latest
mkdir -p ~/caddy-recording
cat > ~/caddy-recording/Caddyfile << 'EOF'
<RECORDING_HOSTNAME> {
    reverse_proxy 127.0.0.1:1234 {
        header_up Host {host}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
    }
}
EOF

sudo podman run -d --name="caddy-recording" \
  --network=host \
  -v ~/caddy-recording/Caddyfile:/etc/caddy/Caddyfile:Z \
  -v caddy_data:/data \
  -v caddy_config:/config \
  --restart always \
  docker.io/library/caddy:latest

Now, go to https://<NEXTCLOUD_DOMAIN>/settings/admin/talk

Under "Recording backend", add:

Recording backend URL: https://<RECORDING_HOSTNAME>
Recording secret: <RECORDING_SECRET>

Also make sure to tick the "The consent to be recorded will be required for each participant before joining every call." option if it's required in your jurisdiction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment