Host Your Private Matrix Server (Synapse)

If you care about privacy, you probably already know the Matrix protocol.
The federated structure of the decentralized network loosely resembles IRC networks.
Matrix ships with spaces, rooms, user management, and much more.

Most of you don’t host it because of its complexity.
I break it down to simple, actionable steps.

Prerequisites

  1. Debian or compatible system.
  2. root or sudo permission.
  3. DNS record pointing to the server.
  4. Python virtual environments.
  5. Postgresql DB backend (optional, but recommended).
  6. Nginx (reverse proxy, TLS, recommended).
  7. certbot for Let’s Encrypt.

Read and examine the Synapse server documentation.

Install The Python Environment

You will use Python and PyPi to install the Synapse matrix server later.

sudo apt install -y python3 python3-pip python3-venv

Install The Postgresql Packages

Postgresql is the recommended DB backend for production Synapse servers.

sudo apt install postgresql libpq-dev

Configure The Postgresql User And DB

Create a dedicated user and the Synapse database.

sudo -u postgres bash
createuser --pwprompt synapse_user
createdb --encoding=UTF8 --locale=C --template=template0 --owner=synapse_user synapse
exit

Install The Nginx

Nginx will give you TLS termination as a reverse proxy.
Moreover nginx can handle rate limiting as well.

sudo apt install nginx

Install certbot

Certbot will handle the Let’s Encrypt certificates.

sudo apt install certbot python3-certbot-nginx

Create The Synapse User

A user (non-root) will be dedicated for the Synapse service.

useradd -m -d /var/lib/synapse -s /bin/bash synapse

Create The Virtual Environment

Create the Python virtual environment in the synapse user’s home.
Do this as the synapse user.

sudo -iu synapse
mkdir -p /var/lib/synapse
cd /var/lib/synapse
python3 -m venv .venv
source .venv/bin/activate

Install The Synapse Software

After activated the venv, you can install Synapse with pip.

pip install --upgrade pip
pip install "matrix-synapse[postgres]"

Generate The Initial Configuration

You generate the initial configuration with a single command.

python3 -m synapse.app.homeserver \
--server-name tomsitcafe.com \
--config-path homeserver.yaml \
--generate-config \
--report-stats=no

It generates a basic configuration and the server keys.

  • registration_shared_secret
  • macaroon_secretkey
  • form_secret
  • signing_keypath (signing key)

Save these keys in a credential store safely.

Configure Your Synapse Homeserver

Visit the end of the article for a full working configuration example.

Server Name And URL

server_name: "tomsitcafe.com" # Domain realm for your instance
public_baseurl: https://matrix.tomsitcafe.com # Users find your server here

Listener

listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
bind_addresses: ['127.0.0.1'] # Only local, nginx will serve https
resources:
- names: [client]
compress: false

Database

database:
name: psycopg2
args:
user: <user>
password: <pass>
dbname: <db>
host: localhost
cp_min: 5
cp_max: 10
keepalives_idle: 10
keepalives_interval: 10
keepalives_count: 3

Configure The Nginx Proxy

For rate limiting set zones in the /etc/nginx/nginx.conf:

# General API (generous)
limit_req_zone $binary_remote_addr zone=matrix_zone:10m rate=300r/m;
# Auth (tight)
limit_req_zone $binary_remote_addr zone=auth_zone:10m rate=10r/m;
# Make throttled requests return 429
limit_req_status 429;

Create a matrix site configuration in /etc/nginx/sites-available/matrix:

server {
listen 80;
listen [::]:80;
server_name matrix.tomsitcafe.com;
location /.well-known/matrix/client {
default_type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '{"m.homeserver": {"base_url": "https://matrix.tomsitcafe.com"}}';
}
# Strict: limit brute-force attempts for login/register
location ~ ^/_matrix/client/(v3|r0)/(login|register)$ {
limit_req zone=auth_zone burst=6 nodelay;
proxy_pass http://127.0.0.1:8008;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_http_version 1.1;
client_max_body_size 50M;
}
# General API: higher allowance
location ~ ^(/_matrix|/_synapse/client) {
limit_req zone=matrix_zone burst=100;
proxy_pass http://127.0.0.1:8008;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
client_max_body_size 50M;
proxy_http_version 1.1;
}
}

It’s port 80 only because certbot will handle the port 443 TLS config with Let’s Encrypt.

Enable the matrix site.

sudo ln -s /etc/nginx/sites-available/matrix /etc/nginx/sites-enabled/
sudo systemctl reload nginx

Obtain The Let’s Encrypt Certificates

Use certbot to obtain the Let’s Encrypt certificates for your domain.
The port 80/tcp must be open for the ACME challenge.

Example:

sudo certbot --nginx -d matrix.tomsitcafe.com

Change the domain to your own one, follow the instructions.

The certificates will be installed under /etc/letsencrypt.

Firewall Configuration

sudo ufw allow 80
sudo ufw allow 443
sudo ufw enable

certbot requires port 80 for cert renew.

Test The Server Configuration

Run the following commands as synapse user to verify the configuration works.

cd /var/lib/synapse
source .venv/bin/activate
synctl start

Create A Systemd Synapse Unit

In /etc/systemd/system/synapse.service:

# /etc/systemd/system/synapse.service
[Unit]
Description=Matrix Synapse Server
After=network.target
Requires=postgresql.service
[Service]
Type=forking
User=synapse
WorkingDirectory=/var/lib/synapse
ExecStart=/var/lib/synapse/.venv/bin/synctl start
ExecStop=/var/lib/synapse/.venv/bin/synctl stop
PIDFile=/var/lib/synapse/homeserver.pid
Restart=on-failure
[Install]
WantedBy=multi-user.target
#+end_example
Reload systemd & enable, start Synapse:
#+begin_src shell
sudo systemctl daemon-reload
sudo systemctl enable synapse
sudo systemctl start synapse
sudo systemctl status synapse

Example Homeserver Configuration

Example homeserver.yaml configuration:

server_name: "tomsitcafe.com"
pid_file: /var/lib/synapse/homeserver.pid
public_baseurl: https://matrix.tomsitcafe.com
suppress_key_server_warning: true
serve_client_wellknown: true
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
bind_addresses: ['127.0.0.1']
resources:
- names: [client]
compress: false
tls_certificate_path: null
tls_private_key_path: null
database:
name: psycopg2
args:
user: <user>
password: <pass>
dbname: <db>
host: localhost
cp_min: 5
cp_max: 10
keepalives_idle: 10
keepalives_interval: 10
keepalives_count: 3
log_config: "/var/lib/synapse/tomsitcafe.com.log.config"
media_store_path: /var/lib/synapse/media_store
registration_shared_secret: "<secret1>"
report_stats: false
macaroon_secret_key: "<key>"
form_secret: "<secret2>"
signing_key_path: "/var/lib/synapse/tomsitcafe.com.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
federation_domain_whitelist: []
federation_ip_range_blacklist: ["0.0.0.0/0"]
forgotten_room_retention_period: 28d
user_ips_max_age: 14d
media_retention:
local_media_lifetime: 90d
remote_media_lifetime: 14d
delete_stale_devices_after: 30d

Final Thoughts

This Synapse setup is very basic and secure.

  • Federation is blocked.
  • Telemetry is disabled.
  • The homeserver is private.
  • Ratelimiting is configured.

Possible next operational steps:

  1. Automatic backups.
  2. Fail2ban configuration.
  3. Gateways and bots.

Privacy is not hiding. It’s deciding what to show.


The Silent Architect IRC network is a quiet place.

Discussion is slow.
Silence is normal.
People speak when they have something worth saying.

Idle minds are welcome.
Noise is not.

Server: irc.silentarchitect.org
Port: 6697 (TLS)
Channel: #ghostops

Discord invite link: https://discord.gg/nxvna45STM


Discover more from Tom's IT Cafe

Subscribe to get the latest posts sent to your email.

Leave a comment