23 July 2026

Self Hosting Element Call

by Sam Hadow

In this blog post I’ll explain how to setup element call using synapse as a matrix homeserver. This blog post assumes you already followed this one.

steps

1. enabling experimental features

in the homeserver.yaml in your synapse homeserver add the following:

experimental_features:
  # MSC3266: Room summary API. Used for knocking over federation
  msc3266_enabled: true
  # MSC4222 needed for syncv2 state_after. This allow clients to
  # correctly track the state of the room.
  msc4222_enabled: true

# The maximum allowed duration by which sent events can be delayed, as
# per MSC4140.
max_event_delay_duration: 24h

rc_message:
  # This needs to match at least e2ee key sharing frequency plus a bit of headroom
  # Note key sharing events are bursty
  per_second: 0.5
  burst_count: 30

rc_delayed_event_mgmt:
  # This needs to match at least the heart-beat frequency plus a bit of headroom
  # Currently the heart-beat is every 5 seconds which translates into a rate of 0.2Hz
  per_second: 1
  burst_count: 20

2. generating keys for the livekit server

run:

podman run --rm livekit/livekit-server:latest generate-keys

And write down the API key and API secret.

3. running the containers for the livekit

Don’t forget to replace YOUR_PUBLIC_IP, YOUR_API_KEY and YOUR_API_SECRET

mkdir -p /home/data/podman/element-call

in /home/data/podman/element-call/config-livekit.yaml:

port: 7880

bind_addresses:
  - "0.0.0.0"

rtc:
  tcp_port: 7881
  port_range_start: 50100
  port_range_end: 50200

  node_ip: YOUR_PUBLIC_IP

room:
  auto_create: false

logging:
  level: info

turn:
  enabled: false

keys:
  YOUR_API_KEY: YOUR_API_SECRET

For the containers replace matrixrtc.hadow.fr, hadow.fr and synapse.hadow.fr with your own domains.

podman run -d \
    --pod=synapse \
    --name=element-call-jwt \
    --restart=unless-stopped \
    -e LIVEKIT_JWT_BIND=:8080 \
    -e LIVEKIT_URL=wss://matrixrtc.hadow.fr/livekit/sfu \
    -e LIVEKIT_KEY=YOUR_API_KEY \
    -e LIVEKIT_SECRET=YOUR_API_SECRET \
    -e LIVEKIT_FULL_ACCESS_HOMESERVERS=hadow.fr \
    -e MATRIX_HOMESERVER_URL=https://synapse.hadow.fr \
    ghcr.io/element-hq/lk-jwt-service:latest
podman run -d \
    --pod=synapse \
    --name=element-call-livekit \
    --restart=unless-stopped \
    -v /home/data/podman/element-call/config-livekit.yaml:/etc/livekit.yaml:ro,Z \
    docker.io/livekit/livekit-server:latest \
    --config /etc/livekit.yaml

You can now regenerate the files for the systemd services:

cd ~/.config/systemd/user/
podman generate systemd --restart-policy=on-failure --files --new --name synapse

systemctl --user daemon-reload
systemctl --user restart pod-synapse.service

Then complete in pod-synapse.service with the ports:

    -p 8008:8008 \
    -p 7880:7880 \
    -p 7881:7881 \
    -p 50100-50200:50100-50200/udp \
    -p 8070:8080 \
    -m=2048m

Don’t forget to open the UDP range 50100-50200 and the TCP port 7881 in your firewall. For example with nftables:

        #element call
        tcp dport 7881 accept
        udp dport 50100-50200 accept

4. nginx configuration files

In a nginx configuration file (for example in /etc/nginx/sites-available/matrixrtc.conf):

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name matrixrtc.hadow.fr;

    ssl_certificate /etc/letsencrypt/live/hadow.fr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hadow.fr/privkey.pem;

    location ^~ /livekit/jwt/ {
      proxy_set_header Host $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;
      proxy_pass http://127.0.0.1:8070/;
    }

    location ^~ /livekit/sfu/ {
      proxy_set_header Host $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;
      proxy_send_timeout 120;
      proxy_read_timeout 120;
      proxy_buffering off;
      proxy_set_header Accept-Encoding gzip;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_pass http://127.0.0.1:7880/;
    }
}

In your well known in /etc/nginx/sites-available/http.conf modify the snippet with the following:

    location /.well-known/matrix/client {
        return 200 '{"m.homeserver": {"base_url": "https://synapse.hadow.fr"},"m.identity_server": {"base_url": "https://vector.im"},"org.matrix.msc4143.rtc_foci": [{"type": "livekit","livekit_service_url": "https://matrixrtc.hadow.fr/livekit/jwt"}]}';
        add_header Content-Type application/json;
        add_header "Access-Control-Allow-Origin" *;
    }
tags: messaging - podman - sysadmin
Copyright (c) July 2026 Sam Hadow Verbatim copying and redistribution of this entire page are permitted provided this notice is preserved.