Create a Chat Server Using Matrix Synapse and Element on Debian 11
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
- Deploy a Debian 11 instance at Vultr
- IPv4 address to optionally download Element and Coturn
- Update the server
- Create a non-root user with sudo privileges
- Point
matrix
,element
, andturn
subdomains to your server
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.