This tutorial shows how to install your own synapse
chat server along with coturn
server for making video/voice calls on Fedora Linux 41.
Update system.
sudo dnf update -y
Set your timezone.
sudo timedatectl set-timezone UTC && date
Install nano text editor, a nice monitor, and terminal multiplexer.
sudo dnf install -y nano htop tmux
sudo reboot
Install synapse
server and nginx
to act as proxy:
sudo dnf install -y matrix-synapse nginx python3-lxml
sudo synapse_homeserver -c /etc/synapse/homeserver.yaml \
--generate-config \
--server-name chat.mydomain.com \
--data-directory=/var/db/synapse \
--report-stats=no \
--enable-registration
These settings will enable registrations and will not report metrics.
The /etc/synapse/homeserver.yaml
config file should look something like this (add what is missing):
# Configuration file for Synapse.
#
# This is a YAML file: see [1] for a quick introduction. Note in particular
# that *indentation is important*: all the elements of a list or dictionary
# should have the same indentation.
#
# [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
#
# For more information on how to configure Synapse, including a complete accounting of
# each option, go to docs/usage/configuration/config_documentation.md or
# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html
server_name: "chat.mydomain.com"
pid_file: /var/run/synapse/homeserver.pid
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
bind_addresses: ['::1', '127.0.0.1']
resources:
- names: [client, federation]
compress: false
database:
name: sqlite3
args:
database: /var/db/synapse/homeserver.db
log_config: "/etc/synapse/chat.mydomain.com.log.config"
media_store_path: /var/db/synapse/media_store
registration_shared_secret: "secret_key"
report_stats: false
macaroon_secret_key: "secret_key"
form_secret: "secret_key"
signing_key_path: "/etc/synapse/chat.mydomain.com.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
enable_registration: true
enable_registration_without_verification: true
# URL preview settings
url_preview_enabled: true
url_preview_ip_range_blacklist:
- '127.0.0.0/8' # Localhost
- '10.0.0.0/8' # Private network
- '172.16.0.0/12' # Private network
- '192.168.0.0/16' # Private network
- '100.64.0.0/10' # Carrier-grade NAT
- '169.254.0.0/16' # Link-local
- '::1/128' # IPv6 localhost
- 'fc00::/7' # IPv6 private network
url_preview_url_blacklist: []
max_spider_size: 10M # Maximum size of pages to fetch (default: 10MB)
# vim:ft=yaml
Ofcourse the enable_registration
and enable_registration_without_verification
fields are to your own discretion
Notice that secret_key
are different and they should be generated by the config generation script
-- don't copy/paste these content directly!
For different database settings consult official docs, we are using SQLite3 because it's easy for the sake of this tutorial. However, server with lots of users probably should use different option. Having said that, SQLite works greate for the use-case of a few users.
Edit the log configuration that was just generated and set handlers.file.filename
to /var/log/synapse/homeserver.log
:
sudo nano /etc/synapse/chat.mydomain.com.log.config
Reset permissions for /etc/synpase
directory contents assuming config was created by the root
user:
chown synapse:synapse /etc/synapse/*
Create directory to store data and media and give synapse
user group write access:
sudo mkdir -p /var/db/synapse/media_store
sudo chown -R root:synapse /var/db/synapse/
sudo chmod -R g+w /var/db/synapse/
Create directory to store log files and give synapse
user group write access:
sudo mkdir /var/log/synapse
sudo chown root:synapse /var/log/synapse
sudo chmod g+w /var/log/synapse
Start synapse
server and check status:
sudo systemctl enable --now synapse
sudo systemctl status synapse
Check the logs that everything is okay:
cat /var/log/synapse/homeserver.log
Check the server datafiles are stored correctly:
ls -al /var/db/synapse/
Create server configuration file:
sudo nano /etc/nginx/conf.d/mydomain.conf
Paste the following configuration inside:
server {
listen 80;
listen [::]:80;
server_name chat.mydomain.com;
root /var/www/synapse;
location / {
try_files $uri $uri/ /index.html;
}
}
Start nginx
server and check status:
sudo systemctl enable --now nginx
sudo systemctl status nginx
Create a simple index.html
page and web directory:
Create directory for web content root:
sudo mkdir -p /var/www/synapse
sudo cat << EOF > /var/www/synapse/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>welcome</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>hey!</body>
</html>
EOF
Set the correct content type for SELinux:
sudo chcon -R -t httpd_sys_content_t /var/www
We are using nginx
as proxy backend for the synapse
server. This allows us to expose the server in a more secure manner and easily manage TLS traffic. This is the reason synapse
is configured for HTTP and not HTTPS, nginx
provides the security layer.
If you don't have one already -- generate verified SSL certificates for the server:
sudo dnf install -y certbot python3-certbot-nginx
sudo certbot --nginx -d chat.mydomain.com
Activate certificate renewal timer:
sudo systemctl start certbot-renew.timer
sudo systemctl status certbot-renew.timer
If you used Let's Encrypt it changed your HTTP server to support TLS, but you can discard it's changes and update to the following config using your domain ofc.
sudo nano /etc/nginx/conf.d/mydomain.conf
Put the following content inside and update for you domain:
# HTTPS server block
server {
server_name chat.mydomain.com;
listen 443 ssl;
listen [::]:443 ssl;
# For the federation port
listen 8448 ssl default_server;
listen [::]:8448 ssl default_server;
# Enable HTTP2 transport
http2 on;
# SSL configuration (adjust paths to your certificates)
ssl_certificate /etc/letsencrypt/live/chat.mydomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/chat.mydomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Matrix routing
location ~ ^(/_matrix|/_synapse/client) {
# note: do not add a path (even a single /) after the port in `proxy_pass`,
# otherwise nginx will canonicalise the URI and cause signature verification
# errors.
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;
# Nginx by default only allows file uploads up to 1M in size
# Increase client_max_body_size to match max_upload_size defined in homeserver.yaml
client_max_body_size 50M;
# Synapse responses may be chunked, which is an HTTP/1.1 feature.
proxy_http_version 1.1;
}
# Serve static files or a webroot for the root path "/"
root /var/www/synapse;
index index.html index.htm;
# Serve the webroot for all other requests
location / {
try_files $uri $uri/ /index.html;
}
}
# HTTP server block (redirect)
server {
server_name chat.mydomain.com;
listen 80;
listen [::]:80;
# location / {
# try_files $uri $uri/ /index.html;
# }
# Redirect all HTTP traffic to HTTPS
return 301 https://$host$request_uri;
}
sudo systemctl restart nginx
and test the configuration.
This server is 100% optional, only if you want to make super private audio/video calls
A TURN (Traversal Using Relays around NAT) server facilitates communication between devices in WebRTC applications when direct peer-to-peer connections are blocked by NATs or firewalls. It acts as a relay, forwarding data between peers that cannot establish a direct connection due to restrictive network configurations. When a client behind a NAT initiates a connection, the TURN server provides a public IP address and port for relaying traffic, ensuring connectivity even in challenging network environments. This comes at the cost of increased latency and bandwidth usage compared to direct connections, but it ensures reliable communication when other methods, like STUN, fail.
Install EPEL package repository and coturn
server:
sudo dnf install -y coturn sqlite
Install secret key generation utility:
sudo dnf install -y pwgen
sudo nano /etc/coturn/turnserver.conf
Update the following configuration fields (uncomment or add):
listening-port=3478
tls-listening-port=5349
# lsiten to all addresses
listening-ip=0.0.0.0
listening-ip=::
# configure external/internal VPS IP
external-ip=134.122.60.115/10.133.0.2
min-port=49152
max-port=65535
use-auth-secret
# shared secret key generated with 'pwgen -s 64 1'
static-auth-secret=DvZPQRfbidYcFGlRdZDM5a3ilkoUmmDIT9GHUiphQ5YOcI60IMkj9xX7JVLkn9RT
# the default settings is for SQLite database.
# other database configuration types in the configuration file.
userdb=/var/db/turndb
realm=chat.mydomain.com
user-quota=4
total-quota=100
no-tcp-relay
# if you have SSL certificate use it here.
# you can create one with Lets Encrypt.
cert=/etc/pki/coturn/public/turn_server_cert.pem
pkey=/etc/pki/coturn/private/turn_server_pkey.pem
log-file=/var/log/coturn/turnserver.log
simple-log
There are some more useful setting in the file, read the descriptions.
sudo sqlite3 /var/db/turndb < /usr/share/coturn/schema.sql
sudo chown root:coturn /var/db/turndb
sudo chmod g+w /var/db/turndb
sudo systemctl start coturn
sudo systemctl enable coturn
sudo systemctl status coturn
You can also check the server logs:
cat /var/log/coturn/turnserver.log
sudo nano /etc/synapse/homeserver.yaml
Add the following lines:
turn_uris:
- "turn:chat.mydomain.com?transport=udp"
- "turn:chat.mydomain.com?transport=tcp"
turn_shared_secret: "your_secret_key_here"
turn_user_lifetime: 86400000
turn_allow_guests: true
remember to use the same secret key as use for the static-auth-secret
field in /etc/coturn/turnserver.conf
systemctl restart synapse
Install and start firewalld
service:
sudo dnf install -y firewalld
systemctl start firewalld.service
Allow HTTP/HTTPS traffic:
sudo firewall-cmd --zone=public --permanent --add-service=http
sudo firewall-cmd --zone=public --permanent --add-service=https
Add rules for coturn
server:
sudo firewall-cmd --zone=public --permanent --add-port=3478/tcp
sudo firewall-cmd --zone=public --permanent --add-port=3478/udp
sudo firewall-cmd --zone=public --permanent --add-port=5349/tcp
sudo firewall-cmd --zone=public --permanent --add-port=5349/udp
sudo firewall-cmd --zone=public --permanent --add-port=49152-65535/udp
Reload the firewall:
sudo firewall-cmd --reload
Confirm and inspect firewall rules:
sudo firewall-cmd --list-all
A typical setup should like like this:
public (default, active)
target: default
ingress-priority: 0
egress-priority: 0
icmp-block-inversion: no
interfaces: eth0 eth1
sources:
services: dhcpv6-client http https mdns ssh
ports: 3478/tcp 3478/udp 5349/tcp 5349/udp 49152-65535/udp
protocols:
forward: yes
masquerade: yes
forward-ports:
source-ports:
icmp-blocks:
rich rules:
That is it! Note that the following configuration should allow you making private and secure 1-on-1 video and audio calls on your synapse server. It might even enable some limited conference calls, however to have the full conference call functionality a more advanced setup is required, and more services. I might add that in future updates.
Remember! Synapse will use the local network for making calls if such option is detected, so to test peer-to-peer call properly client should be on different networks, for example, mobile network not WiFi!