Files
homelab-optimized/docs/services/individual/matrixrtc-livekit.md
Gitea Mirror Bot 75d4f4e02b
Some checks failed
Documentation / Build Docusaurus (push) Failing after 5m0s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-03-29 13:33:25 UTC
2026-03-29 13:33:25 +00:00

5.4 KiB

MatrixRTC / LiveKit — Element X Calls

Last updated: 2026-03-19

MatrixRTC enables voice/video calls in Element X using a LiveKit SFU backend. Both homeservers (mx.vish.gg and matrix.thevish.io) share the same LiveKit SFU on matrix-ubuntu.


Service Overview

Property Value
Host matrix-ubuntu (192.168.0.154)
Matrix homeservers mx.vish.gg (synapse-mx.service) and matrix.thevish.io (synapse.service) — both on Synapse 1.148.0
Compose file hosts/vms/matrix-ubuntu/livekit.yml (deployed manually at /opt/livekit/)
LiveKit version 1.9.12
JWT service ghcr.io/element-hq/lk-jwt-service:latest-ci

Architecture

Element X  ──→  mx.vish.gg (.well-known)         ──→  livekit.mx.vish.gg/livekit/jwt  (JWT service)
           ──→  matrix.thevish.io (.well-known)  ──→  livekit.mx.vish.gg/livekit/sfu  (LiveKit SFU)
                                                       livekit.mx.vish.gg/             (LiveKit WS)

Both homeservers share the same LiveKit backend.

  • NPM on Calypso proxies livekit.mx.vish.gg → matrix-ubuntu
    • /livekit/jwt/ → JWT service port 8089 (container 8080)
    • /livekit/sfu/ → LiveKit SFU port 7880
    • / → LiveKit SFU port 7880 (WebSocket for direct connections)
  • DNS: livekit.mx.vish.gg A record unproxied → 184.23.52.14 (home WAN)
  • TLS: Let's Encrypt cert issued via Cloudflare DNS challenge on matrix-ubuntu, copied to NPM as npm-7

Endpoints

Endpoint URL Purpose
JWT service healthz https://livekit.mx.vish.gg/livekit/jwt/healthz Health check
JWT service SFU get https://livekit.mx.vish.gg/livekit/jwt/sfu/get Token exchange
LiveKit SFU WS wss://livekit.mx.vish.gg/livekit/sfu/ WebSocket signalling
LiveKit HTTP https://livekit.mx.vish.gg/ SFU API
.well-known https://mx.vish.gg/.well-known/matrix/client RTC foci advertisement

Configuration Files on matrix-ubuntu

File Purpose
/opt/livekit/docker-compose.yml LiveKit + JWT service deployment
/opt/livekit/livekit.yaml LiveKit SFU config (keys, RTC ports, external IP)
/opt/synapse-mx/homeserver.yaml Synapse config (MSCs, rate limits)
/etc/nginx/sites-available/mx-vish-gg nginx serving .well-known and Element static files
/etc/letsencrypt/live/livekit.mx.vish.gg/ TLS cert (auto-renews, copies to NPM via deploy hook)

Synapse homeserver.yaml additions

# MatrixRTC / Element Call support
experimental_features:
  msc3266_enabled: true   # Room Summary API (knocking over federation)
  msc4222_enabled: true   # state_after in sync v2
  msc4140_enabled: true   # Delayed events (call participation signalling)

max_event_delay_duration: 24h

rc_message:
  per_second: 0.5
  burst_count: 30

rc_delayed_event_mgmt:
  per_second: 1
  burst_count: 20

.well-known/matrix/client

Served by nginx at https://mx.vish.gg/.well-known/matrix/client:

{
  "m.homeserver": {"base_url": "https://mx.vish.gg"},
  "org.matrix.msc4143.rtc_foci": [
    {
      "type": "livekit",
      "livekit_service_url": "https://livekit.mx.vish.gg/livekit/jwt"
    }
  ]
}

LiveKit SFU config (/opt/livekit/livekit.yaml)

Key settings:

  • use_external_ip: true — auto-detects WAN IP 184.23.52.14
  • use_ice_lite: true — optimised for server-side NAT traversal
  • room.auto_create: false — only lk-jwt-service creates rooms (security)
  • RTC ports: 7880 TCP (API/WS), 7881 TCP (RTC), 50000-60000 UDP (media)
  • Note: UDP 50000-60000 port range is NOT currently forwarded on the router — TURN relay is used instead via coturn at turn:mx.vish.gg:3479

TLS Certificate Renewal

Cert is issued on matrix-ubuntu via certbot + Cloudflare DNS plugin. A deploy hook copies it to NPM on Calypso after renewal:

/etc/letsencrypt/renewal-hooks/deploy/copy-to-npm.sh

If the hook fails, manually copy:

ssh matrix-ubuntu
sudo cp /etc/letsencrypt/live/livekit.mx.vish.gg/fullchain.pem \
  /tmp/lk.crt
sudo cp /etc/letsencrypt/live/livekit.mx.vish.gg/privkey.pem \
  /tmp/lk.key
scp -P 62000 /tmp/lk.crt Vish@100.103.48.78:/volume1/docker/nginx-proxy-manager/data/custom_ssl/npm-7/fullchain.pem
scp -P 62000 /tmp/lk.key Vish@100.103.48.78:/volume1/docker/nginx-proxy-manager/data/custom_ssl/npm-7/privkey.pem
ssh -p 62000 Vish@100.103.48.78 'sudo /usr/local/bin/docker exec nginx-proxy-manager nginx -s reload'

Troubleshooting

Calls not working in Element X

  1. Check .well-known is advertising foci: curl https://mx.vish.gg/.well-known/matrix/client
  2. Check JWT service: curl https://livekit.mx.vish.gg/livekit/jwt/healthz
  3. Check LiveKit is running: ssh matrix-ubuntu "sudo docker ps | grep livekit"
  4. Check LiveKit logs: ssh matrix-ubuntu "sudo docker logs livekit 2>&1 | tail -20"

Stuck calls

See How to resolve stuck MatrixRTC calls — usually caused by delayed events not cleaning up.

JWT service returns 400

Normal for unauthenticated requests. Means the service is running correctly.

Restarting services

ssh matrix-ubuntu
cd /opt/livekit
sudo docker compose restart

Restarting Synapse

ssh matrix-ubuntu
sudo systemctl restart synapse-mx.service