Setting Up a Matrix Server on Ubuntu 20.04 – Part 1

For about the past six months or so, I’ve been interested in the open, decentralized communications standard called Matrix. In May of this year, it was announced on TechCrunch that Automattic had invested $4.6 million into the company behind the Matrix standard. The company in question, New Vector (now rebranded as Element) also develop an open-source chat client, which is a rival to Slack, that runs on the Matrix standard. This is the part that made me sit up and take notice. Matt Mullenweg is the CEO of Automattic and the co-founder and BDFL for WordPress. His decision to invest in this open-source communications standard, which offers an alternative to Slack, could very well mean that in the near future, both the internal communication at Automattic and that of the WordPress project, could end up running on Matrix.

For some time now, myself and the other admins of the WP South Africa Slack community, have been considering switching away from Slack. The main reason for this is that Slack doesn’t offer an option for open source communities to get any premium plan features, so unless we’re prepared to pay $8 USD per person for our over 1000 members we’re stuck with the free version. It also means we don’t “own” our open source community’s communication channels. If Slack ever dropped the free plan, which could happen, we’d be stuck up the proverbial creek.

I decided it was time I took a look at what it would take to get a Matrix homeserver set up, what the pros and cons would be, and if it would be possible to migrate all our users over to our own homeserver.

Requirements

In order to set up a Matrix homeserver, I would need a web server instance to host it. Fortunately, I have a Digital Ocean account, so spinning up a new basic VPS droplet with Ubuntu 20.04, 1GB of RAM, 1 vCPU and 25GB of storage at $5 a month was a click of a few buttons. The other requirement was to have a public domain pointing to the IP address of the server. I asked Hugh, who manages the wpsouthafrica.org domain, to set up an A record in the DNS to point the subdomain matrix.wpsouthafrica.org to the new server IP address. You can also use a regular top-level domain (eg domain.com) but if you already have a domain (for example for your community website), using a subdomain for the Matrix server means not needing to purchase a new one.

Initial set up

A note on commands, I’m running all the commands for this server as the root user. If you have access to your web server via another user with sudo privileges, I suggest switching to the root user, it will just make everything easier.

sudo su

The first step with a new server is to make sure the server software is up to date.

apt update && apt upgrade

Then, I needed to ensure that any package dependencies for the matrix-synapse server software are installed.

apt install lsb-release wget apt-transport-https

While there are official matrix-synapse packages in the Ubuntu repositories, the matrix-synapse docs recommend using their own packages. To do that I first had to enable those packages, by adding the GPG key and the matrix-synapse repository for a Debian/Ubuntu-based system.

wget -qO /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" | tee /etc/apt/sources.list.d/matrix-org.list

Once the repository is set up, I can get the latest package updates, which will now include the Matrix ones, and install the matrix-synapse homeserver software.

apt update
apt install matrix-synapse-py3

During the install, I enter the chosen domain (in my case our subdomain) as the server name. I can also choose not to send Anonymous Data Statistics, but that’s entirely up to you.

Once it’s all installed, I start the matrix-synapse server, and enable it to auto-start on system boot.

systemctl start matrix-synapse
systemctl enable matrix-synapse

Configure Matrix Synapse

Firstly, I needed to generate a random string which is used as the Matrix Synapse registration secret. This I did using the following command.

cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1

The server outputs the random key, which I copied and saved somewhere safe for later.

rgzzglba4KiJo5qIMcAUu3eEWsMtV8wu

No, this isn’t the key for our Matrix homeserver! 🙂

The next step is to edit the homeserver.yaml configuration file, which is in the /etc/matrix-synapse directory. For this tutorial, I’m using nano, but you can use whatever CLI text editor you prefer.

nano /etc/matrix-synapse/homeserver.yaml

I searched for and changed the registration_shared_secret entry, and used the randomly generated key created earlier. It’s important to note that the key should be enclosed by double-quotes.

registration_shared_secret: "YC9h8djIiCaCinWxkE2zt9cgvXYGX25P"

I then saved and closed the homeserver.yaml file.

Then, I restarted the matrix-synapse service, which will apply the new configuration.

systemctl restart matrix-synapse

Generate SSL

My next step was setting up the webserver software Nginx as a reverse proxy for the Matrix Synapse server. Before I could do that, I needed to generate an SSL certificate, to make sure the traffic to and from the server is secure. This can be accomplished using a LetsEncrypt certificate, for which I need to install certbot.

apt install certbot

Once certbot is installed, I generated the SSL certificate, using my email address, and the subdomain we have pointing to the IP address of the webserver.

certbot certonly --rsa-key-size 2048 --standalone --agree-tos --no-eff-email --email user@domain.com -d domain.com

Once this is completed, the SSL certificate and chain were saved at /etc/letsencrypt/live/domain.com/fullchain.pem and the SSL key file was been saved at /etc/letsencrypt/live/domain.com/privkey.pem. This information will become useful when I set up Nginx in the next step.

Setup Nginx as a Reverse Proxy

Setting up a reverse proxy allows Matrix clients to connect to your server securely through the default HTTPS port (443) without needing to run Synapse with root privileges. So first I install Nginx.

apt install nginx

Once installed, I create a virtual host file, to manage the incoming connections.

nano /etc/nginx/sites-available/matrix

The virtual host file configuration redirects all traffic from port 80 (HTTP) to port 443 (HTTPS), configures the SSL port with the certificates created earlier, redirects any requests to the /_matrix endpoint on the domain to the Matrix Synpse server, and configures port 8448, which is used by the Matrix Federation APIs to allow Matrix homeservers to communicate with each other.

server {
    listen 80;
    server_name domain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name domain.com;

    ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;

    location /_matrix {
        proxy_pass http://localhost:8008;
        proxy_set_header X-Forwarded-For $remote_addr;
        # 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 10M;
    }
}

# This is used for Matrix Federation
# which is using default TCP port '8448'
server {
    listen 8448 ssl;
    server_name domain.com;

    ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:8008;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}

Once the file is saved and closed, I can enable the Nginx virtual host, and test to make sure there were no issues.

ln -s /etc/nginx/sites-available/matrix /etc/nginx/sites-enabled/
nginx -t

Finally, I restart Nginx, and enable it to start at system boot.

systemctl restart nginx
systemctl enable nginx

UFW Firewall

With the basics set up, it would now be a good idea to add some firewall rules, to ensure the rest of the server ports are locked down. I add the ssh, http, https, and the TCP port ‘8448’ to the UFW firewall using the command below.

for svc in ssh http https 8448
do
ufw allow $svc
done

After the rules are added, I enable the firewall and check the firewall rules, using these two commands.

ufw enable
ufw status numbered

At this point, I usually log into the server via SSH in a new terminal just to be sure I’ve not locked myself out of SSH access.

Test the Matrix Synapse homeserver

If everything is set up correctly, at this point you should be able to browse to the below URL, enter the server domain, and check if a successful call can be made to your homeserver.

https://federationtester.matrix.org/

If you see Success message, congratulations! You’ve successfully set up your Matrix Synapse server. However, there’s still more to be done, which we will dive into in part 2 of this tutorial.

7 comments

  1. Reading both part 1 and 2 are a pretty a good intro to setting up your first Matrix server but there is one big thing I would change about the post.

    Setting the server name is a very important thing to get right because once you set it, it can never be changed later without completely resetting the database and starting over. In general you should set the server name to the same domain you already use for your website or your email address. So if your website is on wpsouthafrica.org or your email addresses are in the format user@wpsouthafrica.org then your Matrix server name should be wpsouthafrica.org.

    You should avoid using a Matrix specific domain such as matrix.wpsouthafrica.org. This would cause you to have usernames like @jon:matrix.wpsouthafrica.org or room aliases like #capetown:matrix.wpsouthafrica.org. Having the matrix subdomain on there is a bit unnecessary and ugly. It would be like having email addresses in the format of @jon:email.wpsouthafrica.org. It’s extra stuff to type for really no good reason.

    Setting your server name to wpsouthafrica.org does NOT mean that you have to mess up any existing websites or services you host there. The server name is the human facing name of your server but you don’t have to serve the Matrix APIs from that domain. The Matrix APIs can delegated from wpsouthafrica.org to another other domain such as matrix.wpsouthafrica.org. See https://github.com/matrix-org/synapse/blob/master/docs/delegate.md for how to do that.

    (Another tip is to just go ahead and setup the Postgres database from the beginning rather than using SQLite to start with and then having to switch to Postgres later)

    1. Thanks Aaron, I was not aware of this. I’ll look at redoing the server this week, and updating the post.

  2. Hi Jonothan, good howto, unsuccessfully tried a number of different ones. Yours worked sweetly. I used to live in Durban 30 years ago, so know the sethafrican approach 🙂

    I am looking for an admin GUI or web GUi or admin tool to create users without going the command line route via ssh.

    Or can I use a mobile client as an admin user to create new users on my home server?

    Any tips?

    1. Howzit! 🙂

      What I’ve been doing is getting folks to install the Element desktop client, and signing up to join my homeserver.

      The steps are:

      1. Install the Element client for your OS here https://element.io/
      2. Open Element, and click the “Create Account” link at the bottom of the sign up screen
      3. Select the Advanced -> Other option, enter the url to your Matrix homeserver as the homeserver URL and click next
      4. Enter thei chosen username and password combination, and click Register

      If you are using Element you can also invite external users to your homeserver via the Start Chat option next to People, apparently, if you invite someone via email they’d be able to sign up, but I’ve not tried that yet.

  3. Hi,

    I followed your instructions and set up the server. However, when I make a call with another user on the same server, the connection could not be established. Element pops up a message: Debug: event type “m.call.negotiate”. If the other user is in the same wifi network, the call can be made.

    Please could you give some suggestions on the problems?

    Many thanks.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.