Using voice / video calls on your Synapse server can be tricky, especially if you have your homeserver setup at home, with a dynamic public IP. Luckily tricky doesn't mean impossible, like the requirements from Synapse itself might suggest.
This Guide will walk you through setting up a Turnserver (coturn in this case) in Kubernetes, behind a dynamic IP.
Setting up TURN
The turnserver needs to be aware of your public IP Address. Unfortunately almost all home ISP connections use a dynamic public IP, so this address changes daily. Turn does not recognize this IP change and will cease to function. To work around this, I created this docker image that will detect the IP change.
First create a folder to store your kubernetes manifests in, with mkdir coturn && cd coturn/
. Next create the configMap for coturn with the following content
apiVersion: v1
kind: ConfigMap
metadata:
name: coturn-conf
data:
coturn.conf: |
log-file=stdout
#Use your INTERAL IP under which the K8 Host will be reachable through
relay-ip={{INTERNAL_IP_OF_K8_Host}}
#If dont want to use TLS replace with listening-port=5349
tls-listening-port=5349
min-port=49160
max-port=49200
use-auth-secret
static-auth-secret=SuperSecureSecretCHANGEME
#This domain needs to point to your Public IP under which coturn will be reachable through
realm=turn.example.com:5349
#If you want to disable TLS delete the next two lines
cert=/var/run/secret/tls.crt
pkey=/var/run/secret/tls.key
no-cli
Note: It's important, that you set the relay IP correctly. If you have a multi node cluster, it is necessary to pin coturn to a specific host, so that you can be sure which internal IP to point it to. There is no need to specifiy the external / public IP, as the POD will automatically detect it.
Next the deployment itself
apiVersion: apps/v1
kind: Deployment
metadata:
name: coturn
spec:
selector:
matchLabels:
app: coturn
strategy:
type: Recreate
template:
metadata:
labels:
app: coturn
spec:
hostNetwork: true
containers:
- image: dexxter1911/dynamic-coturn:latest
name: turn
env:
- name: TURN_CONFIG
value: /etc/coturn.conf
#CHECK_INTERVAL defines the interval seconds between IP checks.
- name: CHECK_INTERVAL
value: "500"
volumeMounts:
- name: cert
mountPath: /var/run/secret
readOnly: true
- name: conf
mountPath: /etc/coturn.conf
subPath: coturn.conf
volumes:
#In my case this is a wildcard certificate which automatically includes turn.example.com
- name: cert
secret:
secretName: example.com
- name: conf
configMap:
name: coturn-conf
items:
- key: coturn.conf
path: coturn.conf
Note: This deployment will run on the host network itself. This is to avoid multiple NATs and the need to port forward a lot of ports through a service. It might be possible to use a service, I haven't tried though.
Now apply the manifests with
kubectl apply -f configMap.yaml
kubectl apply -f deployment.yaml
Synapse configuration
Locate your homeserver.yaml
configuration file, search for TURN
and add the following config
## TURN ##
# The public URIs of the TURN server to give to clients
#
turn_uris:
- "turns:turn.example.com:5349?transport=tcp"
- "turns:turn.example.com:5349?transport=udp"
- "turn:turn.example.com:5349?transport=tcp"
- "turn:turn.example.com:5349?transport=udp"
# The shared secret used to compute passwords for the TURN server
#
turn_shared_secret: "{{TheSecretYouSetInTheTurnConfigMap}}"
# How long generated TURN credentials last
#
turn_user_lifetime: 1h
Note: If you do not use TLS just omit the lines starting with turns:
.
After you made the change, save and exit the config and restart Synapse. Lastly you need to portforward a few Ports from your Router:
- 5349/udp
- 5349/tcp
- 49160 - 49200/udp
They need to be forwarded to the host that is running the Turnserver POD. That's it! All that is left is verifying that it works. This can be done with this site. Input your Synapse domain and either a User / Password combination or an access token. You can get an access token through Element Desktop: All Settings -> Help & About
; scroll down; click <click to reveal>
next to Access Token.

Your setup is working if you see something like this.