# Installing self-hosted Synapse (Matrix protocol server) and using it with Element messenger

Published: 17/06/2021
Updated: 17/06/2021


This article very briefly describes the process of installing Synapse on CentOS 8. Why would you want to do this is totally up to you.

Installing libs and dependencies

First of all, install necessary libs.

sudo dnf install libtiff-devel libjpeg-devel libzip-devel freetype-devel \
  lcms2 libwebp-devel tcl-devel tk-devel redhat-rpm-config \
  python36 virtualenv libffi-devel openssl-devel
sudo dnf group install "Development Tools"

Installing Synapse server

Next, install the Synapse server itself. I prefer to keep it under opt, you might have other preferences though.

mkdir -p /opt/synapse
virtualenv -p python3 /opt/synapse/env
source /opt/synapse/env/bin/activate
pip install --upgrade pip virtualenv six packaging appdirs
pip install --upgrade setuptools
pip install matrix-synapse

Configuring Synapse server

Now, let’s generate some default config. Please, use your actual server domain name in place of the mock matrix.example.com

source /opt/synapse/env/bin/activate
pip install -U matrix-synapse
cd /opt/synapse
python -m synapse.app.homeserver \
  --server-name matrix.example.com \
  --config-path homeserver.yaml \
  --generate-config \
  --report-stats=no

The output should be looking like this: A config file has been generated in 'homeserver.yaml' for server name 'matrix.example.com'. Please review this file and customise it to your needs.

Install and configure database

Synapse supports different kinds of databases. In this case, I use PostgreSQL.

database:
  name: psycopg2
  args:
    user: synapse_user
    password: BIGSECRETPASSWORD
    database: synapse
    host: localhost
    port: 5432
    cp_min: 5
    cp_max: 10

Install and configure Nginx with TLS

Install Nginx web server if you haven’t. Actually, you can use any other web server (e.g. Apache), but I prefer Nginx and os examples refer to it.

sudo dnf install nginx

Now you need TLS certificate for your domain. You can use Letsencrypt free certificate or you can buy it. Anyway, here I presume that you already have the certificate.

Generate SSL dhparam: openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

edit /opt/synapse/homeserver.yaml

find this section and make it looking as below:

  - port: 8008
    tls: false
    bind_addresses: ['127.0.0.1']
    type: http
    x_forwarded: true

edit /etc/nginx/conf.d/matrix.conf

server {
    ssl on;
    listen 443 ssl http2;
    server_name matrix.example.com;
    charset utf-8;
    keepalive_timeout   70;
    ssl_certificate /etc/letsencrypt/live/matrix.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/matrix.example.com/privkey.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers AESGCM:HIGH:!aNULL:!MD5;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 24h;
    ssl_buffer_size 1400;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.4.4 8.8.8.8 valid=300s;
    resolver_timeout 10s;
    add_header Strict-Transport-Security max-age=63072000;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;

    location / {
        proxy_pass http://localhost:8008;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;
        client_max_body_size 50M;
    }
}

server {
    listen 8448 ssl default_server;
    listen [::]:8448 ssl default_server;
    server_name matrix.example.com;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/matrix.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/matrix.example.com/privkey.pem;
    location / {
        proxy_pass http://localhost:8008;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;
        client_max_body_size 50M;
    }
}

server {
    listen 80;
    server_name matrix.example.com;
    keepalive_timeout   70;
    add_header X-Frame-Options DENY;
    location / {
      return 301 https://matrix.example.com$request_uri;
    }
}

Don’t forget to change matrix.example.com to the actual domain name you use for the server.

Run nginx

sudo systemctl restart nginx
sudo systemctl enable nginx

Run Synapse server

Create /etc/systemd/system/synapse.service

Put the following text into the file:

[Unit]
Description=Matrix Synapse service
After=network.target

[Service]
Type=forking
WorkingDirectory=/opt/synapse/
ExecStart=/opt/synapse/env/bin/synctl start
ExecStop=/opt/synapse/env/bin/synctl stop
ExecReload=/opt/synapse/env/bin/synctl restart
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=synapse

[Install]
WantedBy=multi-user.target

Run the Synapse server

sudo systemctl enable synapse
sudo systemctl start synapse

Now navigate to https://matrix.example.com and make sure that you can see a web page with “It works! Synapse is running” - it means everything goes well.

Registering a new user

Let’s register a new user on the Snapse server. Also, this first user probably should have admin permissions.

register_new_matrix_user -c homeserver.yaml http://localhost:8008

It will ask you for the user name, user password, and if the user should be admin.

Configure the client

Now go to https://element.io/get-started and download the client. Use the server URL (e.g. https://matrix.example.com) and the user/password combination to log in.

Now you can use your own private messaging server, but it still can’t serve audio/video calls.

If everything is good, you might want to lower the log severity to save some disk space. Edit /opt/synapse/matrix.example.com.log.config and change all LEVEL entries to ERROR.

Installing TURN server

TURN server is necessary for audio/video calls. So, if you’re not planning to make calls then you can skip this part.

sudo yum install coturn

edit /etc/coturn/turnserver.conf

Generate or buy TLS certificates for the domain name. Basically, you can use same certificates as you used for Synapse above if the TRUN server and the Synapse server share same domain name. Below is what is important in the config.

listening-port=3478
tls-listening-port=5349
min-port=63000
max-port=64535
use-auth-secret
static-auth-secret=YOUR_SECRET_KEY
realm=matrix.example.com
cert=/etc/pki/coturn/public/turn_server_cert.pem
pkey=/etc/pki/coturn/private/turn_server_pkey.pem
no-cli

Now let’s add TURN support to the Synapse server. Edit /opt/synapse/homeserver.yaml

turn_uris:
  - "turns:turn.example.com:5349?transport=udp"
  - "turn:turn.example.com:3478?transport=udp"
  - "turns:turn.example.com:5349?transport=tcp"
  - "turn:turn.example.com:3478?transport=tcp"

turn_shared_secret: "YOUR_SECRET_KEY"
sudo systemctl restart coturn
sudo systemctl enable coturn
sudo systemctl restart synapse

Possible issue with Letsencrypt certificate and TURN server

It might happen that the encrypted TURN communication is not working and then you probably can see this message in coturn logs: session 002000000000000001: closed (2nd stage), user <> realm <turn.example.com> origin <>, local 1.2.3.4:5349, remote 5.6.7.8:31157, reason: TLS/TCP socket buffer operation error (callback)

I have an impression that this error appears only if you use certificates from Letsencrypt. Probably, non-free certificates should mitigate this issue, but I haven’t checked this idea yet.

So, as a temporary solution you can disable TLS on the coturn server (e.g. comment out the tls-listening-port=5349 line) and also disable encrypted TURN communication on Synapse server (e.g. comment out all turns URIs leaving only turn ones). You would need to restart both coturn and synapse servers then, but after that it should start working. Keep in mind that it means that all audio/video calls are not unencrypted (the text messaging is still encrypted though).