Create a Chat Server Using Matrix Synapse and Element on Debian 11

Updated on November 21, 2023
Create a Chat Server Using Matrix Synapse and Element on Debian 11 header image

Matrix is an open standard for decentralized and end-to-end encrypted communication. It federates using homeservers that communicate with each other over the internet. Homeservers store data about their users and the chat history of rooms created (including rooms originating from other homeservers). Due to the nature of decentralization, when the original homeserver that created a room goes offline, other homeservers can continue communication without issues. Because of this design, there is not a single point of failure.

Synapse is a reference implementation of a Matrix homeserver created by the Matrix.org team and is also the most popular, mature, and complete implementation.

When following this guide, replace all occurrences of example.org with your domain name.

Prerequisites

1. Install Synapse

Add the Matrix.org apt repository.

$ sudo wget -O /usr/share/keyrings/matrix-org-archive-keyring.gpg https://packages.matrix.org/debian/matrix-org-archive-keyring.gpg
$ echo "deb [signed-by=/usr/share/keyrings/matrix-org-archive-keyring.gpg] https://packages.matrix.org/debian/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/matrix-org.list

Install Synapse.

$ sudo apt update
$ sudo apt install matrix-synapse-py3

During the installation, enter the name of the homeserver, which forms part of your Matrix ID. You can change this later in /etc/matrix-synapse/conf.d/server_name.yaml.

2. Install and Configure PostgreSQL

While Synapse supports using SQLite as a database, it is not recommended in production because of performance issues.

Install PostgreSQL.

$ sudo apt install postgresql

Log into the PostgreSQL user.

$ sudo -su postgres

Create a user synapse and a database synapse for PostgreSQL.

$ createuser --pwprompt synapse
$ createdb --encoding=UTF8 --locale=C --template=template0 --owner=synapse synapse

Leave the postgres account.

$ exit

3. Install and Configure Nginx with TLS

Synapse supports running standalone without a reverse proxy, but it is not recommended in production.

Install Nginx and Certbot.

$ sudo apt install nginx certbot python3-certbot-nginx

Open the HTTP, HTTPS, and Synapse ports in the firewall.

$ sudo ufw allow http
$ sudo ufw allow https
$ sudo ufw allow 8448

Get a TLS certificate from Let's Encrypt.

$ sudo certbot certonly --nginx -d matrix.example.org -d example.org

Create a new Nginx configuration file.

$ sudo nano /etc/nginx/sites-available/synapse

Add the following lines to the configuration file.

server {
    server_name matrix.example.org;

    # Client port
    listen 80;
    listen [::]:80;

    return 301 https://$host$request_uri;
}

server {
    server_name matrix.example.org;

    # Client port
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    # Federation port
    listen 8448 ssl;
    listen [::]:8448 ssl;

    # TLS configuration
    ssl_certificate /etc/letsencrypt/live/matrix.example.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/matrix.example.org/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    location ~ ^(/_matrix|/_synapse/client) {
            proxy_pass http://localhost:8008;
            proxy_http_version 1.1;

            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Host $host;

            # Default Synapse upload size.
            # If you change max_upload_size in Synapse config, update it here too.
            client_max_body_size 50M;
    }
}

If the DNS records for the Matrix server name set during Synapse installation (example.org) are pointing to IP address of the same server that hosts Synapse (matrix.example.org), add the following lines at the end of /etc/nginx/sites-available/synapse. If not, add the following lines on that other server (example.org) in an appropriate location for Nginx configuration files.

server {
    server_name example.org;

    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    # TLS configuration
    ssl_certificate /etc/letsencrypt/live/matrix.example.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/matrix.example.org/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    # Redirect
    location ~ ^(/_matrix|/_synapse/client) {
            return 301 "https://matrix.example.org$request_uri";
    }

    # Client homeserver autodiscovery
    location /.well-known/matrix/client {
        default_type application/json;
        add_header Access-Control-Allow-Origin *;

        return 200 '{ "m.homeserver": { "base_url": "https://matrix.example.org" } }';
    }

    # Domain delegation
    location /.well-known/matrix/server {
        default_type application/json;
        add_header Access-Control-Allow-Origin *;

        return 200 '{ "m.server": "matrix.example.org" }';
    }
}

Enable configuration.

$ sudo ln -s /etc/nginx/sites-available/synapse /etc/nginx/sites-enabled

Reload Nginx configuration.

$ sudo systemctl reload nginx.service

4. Configure Synapse

You could edit the configuration file at /etc/matrix-synapse/homeserver.yaml, but you should instead place changes in /etc/matrix-synapse/conf.d/ so apt does not ask to overwrite the configuration file after each update of Synapse.

Create a new database configuration file.

$ sudo nano /etc/matrix-synapse/conf.d/database.yaml

Add the following lines to the configuration file. Replace the password value with the password set earlier for the PostgreSQL synapse user. If you are hosting PostgreSQL on a different server, replace localhost with its address.

database:
  name: psycopg2
  args:
    user: synapse
    password: 'password'
    database: synapse
    host: localhost
    cp_min: 5
    cp_max: 10

Create a secret registration key. Keep it secure because anyone with the key can register a new user, even if registration is disabled.

$ echo "registration_shared_secret: '$(cat /dev/urandom | tr -cd '[:alnum:]' | fold -w 256 | head -n 1)'" | sudo tee /etc/matrix-synapse/conf.d/registration_shared_secret.yaml

By default, Synapse enables presence indicators that show if a person is online. This may cause high CPU usage, and many homeservers disable them. To do so, create a new configuration file.

$ sudo nano /etc/matrix-synapse/conf.d/presence.yaml

Add the following lines to the configuration file.

presence:
  enabled: false

Restart Synapse to apply the new configuration.

$ sudo systemctl restart matrix-synapse.service

Create a new Matrix user.

$ register_new_matrix_user -c /etc/matrix-synapse/conf.d/registration_shared_secret.yaml http://localhost:8008

To open public registration, create a new configuration.

$ sudo nano /etc/matrix-synapse/conf.d/registration.yaml

Add the following lines to the configuration file.

enable_registration: true

By default, Synapse does not allow registration without verification. To enable email verification, add the following lines.

registrations_require_3pid:
  - email

email:
  smtp_host: mail.example.org
  smtp_port: 587

  # If mail server has no authentication, skip these 2 lines
  smtp_user: 'noreply@example.org'
  smtp_pass: 'password'

  # Optional, require encryption with STARTTLS
  require_transport_security: true

  app_name: 'Example Chat'  # defines value for %(app)s in notif_from and email subject
  notif_from: "%(app)s <noreply@example.org>"

To not have any form of verification, add the following line.

enable_registration_without_verification: true

Restart Synapse to apply new configuration.

$ sudo systemctl restart matrix-synapse.service

To verify that Synapse is running, open /_matrix/static/ on your Matrix domain in your browser. For example:

https://matrix.example.org/\_matrix/static/

To check if federation with other homeservers is working, open the Federation Tester, type in your Matrix server name, and click Go.

5. Install and Configure Coturn

You usually need a Traversal Using Relays around NAT (TURN) server to get voice and video calls working. If you don't need this functionality, you can skip this section.

Install Coturn.

$ sudo apt install coturn

Open the TURN and UDP firewall ports.

$ sudo ufw allow 3478
$ sudo ufw allow 5349
$ sudo ufw allow 49152:65535/udp

Get a TLS certificate from Let's Encrypt.

$ sudo certbot certonly --nginx -d turn.example.org

Generate an authentication secret.

$ echo "static-auth-secret=$(cat /dev/urandom | tr -cd '[:alnum:]' | fold -w 256 | head -n 1)" | sudo tee /etc/turnserver.conf

Edit the configuration file.

$ sudo nano /etc/turnserver.conf

Add the following lines to the configuration file.

use-auth-secret
realm=turn.example.org
cert=/etc/letsencrypt/live/turn.example.org/fullchain.pem
pkey=/etc/letsencrypt/live/turn.example.org/privkey.pem

# VoIP is UDP, no need for TCP
no-tcp-relay

# Do not allow traffic to private IP ranges
no-multicast-peers
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=192.88.99.0-192.88.99.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
denied-peer-ip=240.0.0.0-255.255.255.255
denied-peer-ip=::1
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff

# Limit number of sessions per user
user-quota=12
# Limit total number of sessions
total-quota=1200

Restart Coturn to reload configuration.

$ sudo systemctl restart coturn.service

Create a new Synapse configuration file.

$ sudo nano /etc/matrix-synapse/conf.d/turn.yaml

Add the following lines to the configuration file. Replace turn_shared_secret value with the value of static-auth-secret from /etc/turnserver.conf.

turn_uris: [ "turn:turn.example.org?transport=udp", "turn:turn.example.org?transport=tcp" ]
turn_shared_secret: 'static-auth-secret'
turn_user_lifetime: 86400000
turn_allow_guests: True

Restart Synapse to apply the new configuration.

$ sudo systemctl restart matrix-synapse.service

7. Using Matrix

Synapse is now configured, and you can use it with any Matrix client. Element is the most popular Matrix client that is available as a hosted web app, desktop, and mobile application.

If you want to host your own instance of Element, read further for instructions on how to install and configure it.

To log in on your Matrix client, type your full Matrix ID (for example, @bob:example.org) in the username field. Most clients like Element automatically fetch the homeserver information. If that does not work, check if the /.well-known/matrix/client/ URL on your Matrix server has the correct homeserver information. If it does, your client does not support homeserver discovery, and you need to insert the homeserver address manually.

8. Install Element

Install jq.

$ sudo apt install jq

Create a directory for Element.

$ sudo mkdir -p /var/www/element

Create a new file for fetching the newest Element release.

$ sudo nano /var/www/element/update.sh

Add the following lines to the file.

#!/bin/sh
set -e

install_location="/var/www/element"
latest="$(curl -s https://api.github.com/repos/vector-im/element-web/releases/latest | jq -r .tag_name)"

cd "$install_location"

[ ! -d "archive" ] && mkdir -p "archive"
[ -d "archive/element-${latest}" ] && rm -r "archive/element-${latest}"
[ -f "archive/element-${latest}.tar.gz" ] && rm "archive/element-${latest}.tar.gz"

wget "https://github.com/vector-im/element-web/releases/download/${latest}/element-${latest}.tar.gz" -P "archive"
tar xf "archive/element-${latest}.tar.gz" -C "archive"

[ -L "${install_location}/current" ] && rm "${install_location}/current"
ln -sf "${install_location}/archive/element-${latest}" "${install_location}/current"
ln -sf "${install_location}/config.json" "${install_location}/current/config.json"

Mark file as executable.

$ sudo chmod +x /var/www/element/update.sh

Execute the file to download Element.

$ sudo /var/www/element/update.sh

To update Element in the future, re-run the command.

9. Configure Element

Copy the sample Element configuration.

$ sudo cp /var/www/element/current/config.sample.json /var/www/element/config.json

Edit the configuration file.

$ sudo nano /var/www/element/config.json

Change the default Matrix.org homeserver address to your homeserver.

"m.homeserver": {
    "base_url": "https://matrix.example.org",
    "server_name": "example.org"
},

If you want to use your own name instead of Element in the website title and other places, change the brand name.

"brand": "My Example Chat",

Get a TLS certificate from Let's Encrypt.

$ sudo certbot certonly --nginx -d element.example.org

Create a new Nginx configuration file.

$ sudo nano /etc/nginx/sites-available/element

Add the following lines to the configuration file.

server {
    listen 80;
    listen [::]:80;

    server_name element.example.org;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name element.example.org;

    root /var/www/element/current;
    index index.html;

    add_header Referrer-Policy "strict-origin" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;

    # TLS configuration
    ssl_certificate /etc/letsencrypt/live/element.example.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/element.example.org/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
}

Enable configuration.

$ sudo ln -s /etc/nginx/sites-available/element /etc/nginx/sites-enabled

Reload the Nginx configuration.

$ sudo systemctl reload nginx.service

You can now access Element from the element subdomain (for example, https://element.example.org). To log in, type your username or full Matrix ID.