Files
tuwunel/docs/matrix_rtc.md
2026-01-20 23:08:56 +00:00

7.5 KiB

This is a basic "step by step" guide to setting up Element Call/MatrixRTC for Tuwunel. The aim is to provide the minimum amount of information necessary to get Element Call working, in a way that can be followed by anyone regardless of technical experience. Each step provides a brief description of what it is doing, followed by a suggested command to complete it. This guide is inspired by this blog post. For further information on setting up Element Call, please refer to that guide.

The following is very much based on my own experience of setting up Element Call for Tuwunel. Any amendments or additions are gratefully received.

Requirements

  • A Linux server.
  • A working Tuwunel installation.
  • Docker and Docker Compose installed on the server.
  • A Caddy or Nginx reverse proxy.
  • A basic knowledge of how the above components work.

Notes

  • My installation is on a Debian server. These instructions should work on other distributions, but I may have missed something.
  • I use Caddy as a reverse proxy, and my Nginx knowledge is a bit rusty, so there is a chance that there are errors in the Nginx configuration provided.
  • yourdomain.com is whatever you have set as server_name in your tuwunel.toml. This needs to be replaced with the actual domain. It is assumed that you will be hosting MatrixRTC at matrix-rtc.yourdomain.com. If you wish to host this service at a different subdomain, this needs to be replaced as well.

1. Set Up DNS

Create a DNS record for matrix-rtc.yourdomain.com pointing to your server.

e.g. an A record for matrix-rtc pointing to your.server.ip.address.

2. Create Docker Containers

  1. Create a directory for your MatrixRTC setup: mkdir /opt/matrix-rtc.
  2. Change directory to your new directory: cd /opt/matrix-rtc.
  3. Create and open a compose.yaml file for MatrixRTC: nano compose.yaml.
  4. Add the following. mrtckey and mrtcsecret should be random strings. It is suggested that mrtckey is 20 characters and mrtcsecret is 64 characters.
services:
  matrix-rtc-jwt:
    image: ghcr.io/element-hq/lk-jwt-service:latest
    container_name: matrix-rtc-jwt
    environment:
      - LIVEKIT_JWT_PORT=8081
      - LIVEKIT_URL=https://matrix-rtc.yourdomain.com/livekit/sfu
      - LIVEKIT_KEY=mrtckey
      - LIVEKIT_SECRET=mrtcsecret
      - LIVEKIT_FULL_ACCESS_HOMESERVERS=yourdomain.com
    restart: unless-stopped
    ports:
      - "8081:8081"

  matrix-rtc-livekit:
    image: livekit/livekit-server:latest
    container_name: matrix-rtc-livekit
    command: --config /etc/livekit.yaml
    ports:
      - 7880:7880/tcp
      - 7881:7881/tcp
      - 50100-50200:50100-50200/udp
    restart: unless-stopped
    volumes:
      - ./livekit.yaml:/etc/livekit.yaml:ro
  1. Close the file: Ctrl+x.
  2. Create and open a livekit.yaml file: nano livekit.yaml.
  3. Add the following. mrtckey and mrtcsecret should be the same as those from the compose.yaml.
port: 7880
bind_addresses:
  - ""
rtc:
  tcp_port: 7881
  port_range_start: 50100
  port_range_end: 50200
  use_external_ip: true
  enable_loopback_candidate: false
keys:
  mrtckey: "mrtcsecret"
  1. Close the file: Ctrl+x.

3. Configure .well-known

3.1. .well-known served by Tuwunel

Follow this step if your .well-known configuration is served by tuwunel. Otherwise follow Step 3.2

  1. Open your tuwunel.toml file: e.g. nano /etc/tuwunel/tuwunel.toml.
  2. Find the line reading #rtc_transports = [] and edit it to be:
rtc_transports = [
  { 
    type = "livekit", 
    livekit_service_url = "https://matrix-rtc.yourdomain.com" 
  }
]
  1. Close the file: Ctrl+x.

3.2. .well-known served independently

Follow this step if you serve your .well-known/matrix files directly. Otherwise follow Step 3.1

  1. Open your .well-known/matrix/client file: e.g. nano /var//www/.well-known/matrix/client.
  2. Add the following:
  "org.matrix.msc4143.rtc_foci": [
    {
      "type": "livekit",
      "livekit_service_url": "https://matrix-rtc.yourdomain.com"
    }
  ]

The final file should look something like this:

{
  "m.homeserver": {
    "base_url":"https://matrix.yourdomain.com"
  },
  "org.matrix.msc4143.rtc_foci": [
    {
      "type": "livekit",
      "livekit_service_url": "https://matrix-rtc.yourdomain.com"
    }
  ]
}

  1. Close the file: Ctrl+x.

4. Configure Firewall

You will need to allow ports 7881/tcp and 50100:50200/udp through your firewall. If you use UFW, the commands are: ufw allow 7881/tcp and ufw allow 50100:50200/udp.

5. Configure Reverse Proxy

As reverse proxies can be installed in different ways, I am not giving step by step instructions for this section. If you use Caddy as your reverse proxy, follow step 5.1. If you use Nginx, follow step 5.2.

5.1. Caddy

  1. The following needs to be added to your Caddyfile. If you are running Caddy in Docker, replace localhost with matrix-rtc-jwt in the first instance, and matrix-rtc-livekit in the second.
matrix-rtc.yourdomain.com {
    # This is matrix-rtc-jwt
    @jwt_service {
        path /sfu/get* /healthz*
    }
    handle @jwt_service {
        reverse_proxy localhost:8081 {
            header_up Host {host}
            header_up X-Forwarded-Server {host}
            header_up X-Real-IP {remote}
            header_up X-Forwarded-For {remote}
            header_up X-Forwarded-Proto {scheme}
        }
    }
    # This is livekit
    handle {
        reverse_proxy localhost:7880 {
            header_up Connection "upgrade"
            header_up Upgrade {http.request.header.Upgrade}
            header_up Host {host}
            header_up X-Forwarded-Server {host}
            header_up X-Real-IP {remote}
            header_up X-Forwarded-For {remote}
            header_up X-Forwarded-Proto {scheme}
        }
    }
}
  1. Restart Caddy.

5.2. Nginx

  1. The following needs to be added to your Nginx configuration:
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name matrix-rtc.yourdomain.com;

    # Logging
    access_log /var/log/nginx/matrix-rtc.yourdomain.com.log;
    error_log /var/log/nginx/matrix-rtc.yourdomain.com.error;

    # TLS example for certificate obtained from Let's Encrypt.
    ssl_certificate /etc/letsencrypt/live/matrix-rtc.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/matrix-rtc.yourdomain.com/privkey.pem;

    # lk-jwt-service
    location ~ ^(/sfu/get|/healthz) {
        proxy_pass http://localhost:8081;

        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Server $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;
    }
    # livekit
    location /livekit/sfu/ {
       proxy_pass http://localhost:7880;
       proxy_http_version 1.1;

       proxy_set_header Connection "upgrade";
       proxy_set_header Upgrade $http_upgrade;

       proxy_set_header Host $host;
       proxy_set_header X-Forwarded-Server $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;

        # Optional timeouts per LiveKit
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }

    # Redirect root / at /livekit/sfu/
    location = / {
        return 301 /livekit/sfu/;
    }
}
  1. Restart Nginx.

6. Start Docker Containers

  1. Ensure you are in your matrix-rtc directory: cd /opt/matrix-rtc.
  2. Start containers: docker compose up -d.

Element Call should now be working.