My wife and I enjoy chatting with my friends on Matrix, but it’s nice to talk to and see friends who we can’t see in person often due to distance. Fortunately Matrix supports voice and video chat. If you’re on the same network this will work without having to setup a relay, but most of the time you will be behind a firewall NAT. In this case you need a TURN server to relay your voice and video chat.

The Synapse project has documentation on how to setup TURN for Matrix and I’ve based my setup on it.

Turn Server Configuration

I added the following to my NixOS configuration. You’ll want to replace turn.example.com with your own domain. You will also want to generate your own <shared-secret>. If you have pwgen installed, you can generate one with:

1
$ pwgen -s 64 1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
{
  services.coturn = {
    enable = true;
    lt-cred-mech = true;
    use-auth-secret = true;
    static-auth-secret = "<shared-secret>";
    realm = "turn.example.com";
    relay-ips = [
      "<public-server-ip>"
    ];
    no-tcp-relay = true;
    extraConfig = "
      cipher-list=\"HIGH\"
      no-loopback-peers
      no-multicast-peers
    ";
    secure-stun = true;
    cert = "/var/lib/acme/turn.example.com/fullchain.pem";
    pkey = "/var/lib/acme/turn.example.com/key.pem";
    min-port = 49152;
    max-port = 49999;
  };

  # Open ports in the firewall.
  networking.firewall = {
    enable = true;
    allowPing = false;
    allowedTCPPorts = [
      5349  # STUN tls
      5350  # STUN tls alt
      80    # http
      443   # https
    ];
    allowedUDPPortRanges = [
      { from=49152; to=49999; } # TURN relay
    ];
  };

  # setup certs
  services.nginx = {
    enable = true;
    virtualHosts = {
      "turn.example.com" = {
        forceSSL = true;
        enableACME = true;
      };
    };
  };

  # share certs with coturn and restart on renewal
  security.acme.certs = {
    "turn.example.com" = {
      group = "turnserver";
      allowKeysForGroup = true;
      postRun = "systemctl reload nginx.service; systemctl restart coturn.service";
    };
  };
}

Matrix Server Configuration

After setting up the TURN server, I added the following configuration to my Matrix server:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  services.matrix-synapse = {
    # ...
    turn_shared_secret = "<shared-secret>";
    turn_uris = [
      "turn:turn.example.com:5349?transport=udp"
      "turn:turn.example.com:5350?transport=udp"
      "turn:turn.example.com:5349?transport=tcp"
      "turn:turn.example.com:5350?transport=tcp"
    ];
  }
}