Setting up Matrix and Riot with docker

In an effort to no longer depend on Facebook Messenger, WhatsApp, and other Hangouts, I have been using Matrix and Riot as much as possible for a while now.

I have been using their public instance, and it's fine most of the time, but it can be quite laggy at rush hour.

The coronavirus epidemic, with everyone stuck at home, certainly hasn't helped either...

It's been on my list of things to try for a long time, and now I finally did it: I setup my own instance with both Synapse and Riot!

Here is a guide, in hope it can be useful to someone, even if this someone might just be future-me. (hi! 👋)

Disclaimer: all the following worked at the time I wrote this, for Synapse v1.11.1 and Riot v1.5.13.

Please adapt as necessary for more recent versions...

Reverse proxy (caddy)

The startup order of all the services might not be too important, but preparing the reverse proxy first will prevent Synapse and Riot from outputting a lot of errors on startup if they can't access and be accessed from the urls they are expecting.

There is some official documentation on the topic of using a reverse proxy in front of Synapse, but I adapted it a bit because I encountered some issues with it and my dockerized setup. A third party Synapse admin web-app also failed to work with this configuration, calling non-existing api endpoints...

Note: I'm using caddy outside docker. To use a dockerized caddy, adapt as needed.

Caddy 1 configurations

Synapse

my.matrix.host { # change: NOT /_matrix because otherwise, # /_synapse does not work on this port and synapse-admin fails to work proxy / http://127.0.0.1:18008 { transparent } } my.matrix.host:8448 { proxy / http://127.0.0.1:18008 { transparent } }

Riot

my.riot.host { proxy / http://127.0.0.1:18010 { transparent } }

Caddy 2 (beta 15) configurations

Synapse

my.matrix.host { # change: NOT /_matrix because otherwise, # /_synapse does not work on this port and synapse-admin fails to work reverse_proxy 127.0.0.1:18008 } my.matrix.host:8448 { reverse_proxy 127.0.0.1:18008 }

Riot

my.riot.host { reverse_proxy 127.0.0.1:18010 }

That's it!

Deploying Synapse

I chose not to use a dedicated database server. SQLite will do for now.

docker-compose.yml :

version: "3.4" services: synapse: container_name: "synapse" image: "matrixdotorg/synapse:v1.11.1-py3" restart: "unless-stopped" ports: - "127.0.0.1:18008:8008" - "127.0.0.1:18448:8448" volumes: - "./data/:/data/"

Synapse will expect its /data mount point to already have a valid configuration.

Synapse configuration file

Generating the initial configuration is documented here, but basically, you just have to run the following:

docker run -it --rm \ -v "/opt/matrix/synapse/data:/data" \ -e SYNAPSE_SERVER_NAME=my.matrix.host \ -e SYNAPSE_REPORT_STATS=yes \ matrixdotorg/synapse:v1.11.1-py3 generate

This will generate a homeserver.yaml file on the container's /data mount point, that should be reviewed and customized as needed.

I basically changed the following:

uncommented and set a value for public_baseurl

uncommented and set a value for user_ips_max_age

uncommented and set a value for all the smtp_* properties, under the email section

properties, under the uncommented and set enable_notifs to true

to uncommented and set a value for client_base_url

I also had to add two properties in the email section, or my instance wouldn't start! (There is an open issue about it): notif_template_html: notif_mail.html notif_template_text: notif_mail.txt



Then docker-compose up -d everything, and you are done!

(Maybe check docker-compose logs -f to make sure everything is all right...)

Deploying Riot

Riot is even easier to setup.

docker-compose.yml :

version: "3.4" services: riot: container_name: "riot" image: "vectorim/riot-web:v1.5.13" restart: "unless-stopped" ports: - "127.0.0.1:18010:80" volumes: - "./config.json/:/app/config.json"

Copy and adapt your config.json from the sample one.

Then docker-compose up -d , et voilà!

User provisioning

By default, new users registration is disabled.

Adding new users was surprisingly difficult, as there is absolutely no admin UI.

Creating an admin user

Synapse comes with a CLI tool ( register_new_matrix_user ) to create users, but it's a bit cumbersome to use, and it cannot set a user's display name or email.

It's nonetheless necessary if only to create the first admin user...

Accessing the command inside the docker container can be done by opening an interactive shell: docker exec -it synapse sh .

Executing register_new_matrix_user -c /data/homeserver.yaml https://my.matrix.host:8448 should then be enough.

The tool will prompt for a user name, password, and whether the new user should be an admin.

Creating other users

My goal was to be able to create a user, set its initial password, display name, and email.

At the time of writing, the third party Synapse admin web app cannot initialize a user's email.

The only thing that worked was curl ... 😶 (and even for that, the api documentation is hard to find)

The following examples are crafted with the help of https://matrix.org/docs/guides/client-server-api to first get an access_token , and https://github.com/matrix-org/synapse/blob/master/docs/admin_api/user_admin_api.rst for the user admin api.

Getting an access_token

curl -XGET "https://my.matrix.host:8448/_matrix/client/r0/login" # You should manually confirm it's a known and expected login flow: { "flows": [ { "type": "m.login.password" } ] }

curl -XPOST -d \ '{"type":"m.login.password", "user":"myadminuser", "password":"mypassword"}' \ "https://my.matrix.host:8448/_matrix/client/r0/login" # Should output something like this: { "access_token": "{ACCESS_TOKEN}", "device_id": "DDKBZUMSNV", "home_server": "my.matrix.host", "user_id": "@myadminuser:my.matrix.host", "well_known": { "m.homeserver": { "base_url": "https://my.matrix.host/" } } }

Using the User Admin API

Once you have an admin access_token , you can do the following:

List users

curl --header "Authorization: Bearer {ACCESS_TOKEN}" -XGET \ https://my.matrix.host:8448/_synapse/admin/v2/users?from=0&limit=10&guests=false

Query a user

curl --header "Authorization: Bearer {ACCESS_TOKEN}" -XGET \ https://my.matrix.host:8448/_synapse/admin/v2/users/@test:my.matrix.host

Modify or create a user

curl --header "Authorization: Bearer {ACCESS_TOKEN}" -XPUT -d \ '{"displayname":"Display Name", "password": "password", "threepids": [{"medium": "email", "address":"useremail@example.com"}] }' \ https://my.matrix.host:8448/_synapse/admin/v2/users/@newuser:my.matrix.host

Initializing other user values

I wanted to enable email notifications for all my new users, but unfortunately, there is no easy way to do that.

(BTW, is that a Matrix feature, or a Riot one? 🤔)

Based on what Riot does when enabling the feature from its UI, A POST to https://my.matrix.host/_matrix/client/r0/pushers/set should be enough, but I'm not sure about how to find all the relevant data the endpoint expects...

Since I don't plan on having too many users, I gave up, and logged in into Riot for each of them to manually enable the setting. 🙂

(Same for adding default rooms, etc. It would be nice, but it's currently easier to do it manually...)

What's next?

In the end, it wasn't too difficult. (but still difficult enough to warrant a guide)

Matrix and Riot are still rough around the edges, but they are getting better and better. I hope the tooling and documentation will follow.

Performance is correct, and without question a lot better than on the public Riot instance. (I'm running everything on a dedicated server powered by an Atom CPU and 2GB or RAM)

Synapse is currently using about 100MB, and Riot about 4MB. (according to docker stats )

The server's load average hasn't changed much.

I wonder how long I will be able to keep my instance running without having to switch to a dedicated database engine...

I also need to setup some kind of automated backup for my data. I hope I will be able to write a post about that later...

Bonus

To check a Matrix/Synapse server version: