This is my personal documentation on how i host and integrate element-call to conduct private video-chats. It assumes that you have traefik setup and running, but provides a working config as example.
This setup looks somewhat like this:
- Traefik as proxy and for ssl-termination
- a matrix instance (synapse) on
matrix.<your-domain>- It provides authorization and manages your rooms/groups
- a matrix-sliding-proxy on
syncv3.<your-domain>if you want to use the Element X app - the element-call webapp on
element-call.<your-domain>(optional since element v1.11.97) - a livekit instance on
livekit.<your-domain>- livekit does the heavy lifting and is basically the videocall-backend to the element-call webapp
The whole setup here is split into multiple compose files. I hope this makes it easier to focus on one topic at a time. On my on server, everything is in one folder and a single compose file.
make sure the required subdomains (see above) point to your server
UPDATE 2025-04
This step is optional since element v1.11.97.
Since that version, the element-call webapp is bundled with the element webapp. See element-hq/element-web#29309.
Setting up the element-call webapp is only necessary if you want to join video-calls via a standalone webapp, without going through a client like element or Element-X.
- Create a folder for the elment-call webapp to live in. In this repo this is the
element-callfolder. If you are like me and have everything matrix related in a single folder, feel free to use that one. - Go into that folder and create an
element-call-config.json. It tells your element-call instance where to find your matrix homeserver. It could also tell element-call where to finde the livekit backend, but the prefered way to set this is the.well-known/matrix/client-file that we will setup later. See the example in this repo for what the element-call-config looks like (you have to replace<your-domain>) - Create a compose.yaml (or use your existing one) and add the
element-callservice as is shown in this repo.
You should now be able to docker compose up -d element-call and visit element-call.<your-domain>. You should already see the element-call login. If you already configured your matrix-server, you might be able to login, though calls won't work yet because livekit is not yet setup.
- Go into the folder that contains the
compose.yamlthat contains the element-call service - Copy over the
livekit.yamlandlivekit-redis.conffrom this repo as a starting point.- Replace
<your-domain>in these files
- Replace
- Add the
livekit,livekit-redisandlivekit-jwt-serviceto thecompose.yamlas shown in this repo- Replace
<your-domain>in the environments and labels of these services - In an ideal world you wouldn't need the livekit-jwt-service. It only provides two routes, and traefik is configured to route only these two to the jwt-service.
- the jwt-service is using the same subdomain as the livekit backend to prevent cors issues
- Replace
- Create a temporary folder somewhere in which you run the livekit/generate container as described in their docs. This will get you a
livekit.yamlwith a key and a secret. - Replace
<livekit-key>and<livekit-secret>with the values from the generatedlivekit.yaml - Remove the temporary folder. It is no longer needed.
- forward the following ports to your server if you have a firewall:
- 3478 (udp, STUN)
- skip this one if you don't want to have your STUN-traffic be unencrypted. STUN allowes clients to find each others public ip address. This is usally not considered sensitive data though.
- 7882 (udp, WebRTC)
- 7881 (tcp, WebRTC-Fallback)
- 50000-50255 (udp, TURN relay range)
- 80 (tcp+udp, http)
- 443 (tcp+udp, https+wss)
- 3478 (udp, STUN)
(see the LiveKit docs for more details on the used ports)
Here is why these Ports can be forwarded (mostly) without exposing unencrypted traffic:
- Port 80 goes to traefik, which just redirects http-traffic to port 443 with encryption
- Port 443 also goes to traefik and is encrypted (HTTPS/wss)
- Port 3478 is the only one actually handling unencrypted traffic (see above)
- 7881 and 7882 Go to livekit. They handle WebRTC-Traffic, which is as part of its spec, must be encrypted. They therfore don't need the ssl-termination through traefik
- Ports 50000-50255 are used to funnel traffic between clients that can't directly connect to each other, but can connect to the server. If i understand it correctly, it should only ever contain WebRTC-Traffic in this scenario, which is always encrypted. (see https://stackoverflow.com/a/23300741)
You should now be able to docker compose up -d These services. It will not be used by element-call yet, because we have to tell element-call where to find the livekit backend. This is done later via the file served at https://matrix.<your-domain>/.well-known/matrix/client
Services that belong to the Element ecosystem want to know where they can find each other. To do so, they usually ask the /.well-known/matrix/client-config of the homeserver.
In order to easily adjust what is served here i decided to create it myself, instead of relying on synapse to create it for me.
To serve the required well-known-configs do the following:
- Add the
well-known-nginxservice to the compose.yaml that contains your synapse service (see this reposmatrix/compose.yaml) - Add the
nginx.conf - replace
<your-domain>in the services labels and thenginx.conf
You should now be able to docker compose up -d well-known-nginx. treafik should then route well-known requests for the homeserver and the syncv3 proxy to that service
If you look inside the nginx.conf you'll see that it serves 2 "well-know" files.
- The
/clientone is so that Element knows where to find the syncv3-proxy, and so that element-call knows where to find the livekit backend - I have to recheck where the
/server-one is used, not sure about that at the moment. Could be Element X related
The standalone Element-Call Webapp should be usable now
- Go to https://element-call.
- Login with a user on your homeserver
- Try starting a call
As per the element-call docs, you can add this to your synapses homeserver.yaml:
experimental_features:
msc3266_enabled: true
msc4222_enabled: true
max_event_delay_duration: 24h
rc_message:
per_second: 0.5
burst_count: 30
rc_delayed_event_mgmt:
per_second: 1
burst_count: 20
MSC3266 is the Room Summary API (Proposal). This option is supposed to only be required for "guest access / knocking" (element-hq/element-call#2343 (comment)). One user had problems with invite-links not working without this setting (element-hq/element-call#2340).
As i only video-call with users on my homeserver, i've had no issues so far that were caused by this feature not being enabled.
Since v25.03.3 Element-X uses the embedded version of element-call, so no further configuration should be necessary.
If you self-host the element-web app (see the element-web/compose.yaml for an example), you can provide a json-config-file. In there you can specify this:
{
"features": {
"feature_video_rooms": true,
"feature_group_calls": true,
"feature_element_call_video_rooms": true
}
}This enables
- video-rooms + element-call-video-rooms (special rooms where the main focus is a perpetuate video call, but that also includes a text-chat)
- group-calls (make sure you have permissions to make element-call calls in your groups)
TODO: The "New video room" buttons seems to still only appear, if a user actively joins the "Video Rooms"-Beta. Is this setting here respected?
The desktop app just wraps element-web. Since v1.11.97 it bundles the element-call webapp, so it should just work.