LangMerah

How I hosted a single-user Dendrite Matrix homeserver on Ubuntu 20.04

Published: 2021-12-27 on Langmerah's blog. (Last revised: 2022-04-01)


Why host a single-user Matrix homeserver?

If you own a personal website, you might have once thought of getting an e-mail address with your domain name attached. This might be done for vanity purposes, where it would suffice using an e-mail forwarding service like Anonaddy or SimpleLogin which lets you use your domain name. Others might insist on setting up their own e-mail server instead, whether to self-host data to avoid privacy and anonimity risks that come with relying on e-mail providers, or simply to learn how to do it out of curiosity.

While hosting an e-mail server is relatively easy, it is marred with a number of complications, like spam torrenting into your inbox and being automatically blacklisted by major e-mail providers. E-mail was developed without end-to-end encryption (E2EE) in mind. Hence, if you want to send e-mails privately, you will have to find providers with E2EE built in. However, the recepient must also be subscribed to one in order for private two-way communication to work. Or, you could encrypt e-mail on the client side using a program like PGP, but it is notoriously difficult to do by non-privacy tech enthusiasts.

An alternative would be Matrix, which has E2EE baked in and easily enabled on a client. It was intended to be an instant messaging protocol, often compared to Discord or WhatsApp, with features like file uploads, profiles, chat backups, rooms, and the newly implemented spaces. I believe it is versatile enough to be used as a viable alternative to e-mail. You can learn more about Matrix here.

In this tutorial, I will be hosting Dendrite, a Matrix homeserver intended to be a lightweight alternative to Synapse, the standard reference homeserver developed by Matrix's core team. Matrix homeservers can be huge resource hogs, so I chose Dendrite, hoping it would perform well on my 2 GB VPS. The biggest drawback of Dendrite is that it lacks some major features found in Synapse. Most notably, push notifications are not implemented yet, but that is not a major problem for me because I only read e-mails in the morning and not when notified.

This tutorial is essentially a regurgitated version of the official instructions on their GitHub page, but I elaborated it to make it more noob-friendly to amateur sysadmins like me. Here, I built the homeserver from source and used systemd to run and daemonise it. It is possible to deploy a Docker container if that is your preferred environment. The following tutorial assumes you are logged in to a non-root user with sudo privileges.

Installing Go

First, you will need Go to build the homeserver. If you do not have it, you can refer to Go's official installation documentation. This is how I installed Go in my system:

First, download the compressed archive of the latest version (1.17.3 at the time of writing) and your system's processor architecture (x64 in my case). Check for the version you need here:

$ wget https://go.dev/dl/go1.17.3.linux-amd64.tar.gz -O ~/go.tar.gz

Perform a checksum on the download. Refer to the previous link to get the actual checksum:

$ sha256sum ~/go.tar.gz
550f9845451c0c94be679faf116291e7807a8d78b43149f9506c1b15eb89008c  ~/go.tar.gz

If the checksums do not match, re-download the compressed archive. Otherwise, extract it to your home directory /usr/local:

$ tar -C /usr/local -xzf ~/go.tar.gz && rm ~/go.tar.gz

Verify if Go has been installed:

$ go version
go version go1.17.3 linux/amd64

If you got the error that Go is not found, add it to the PATH environment variable, then run the previous command to verify again. I did it by modifying and re-sourcing my Z shell's .rc file:

$ echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.zshrc
$ source ~/.zshrc

Creating a database in PostgreSQL

PostgreSQL will be used as our database because of its superior performance. If your system does not have it, simply install it from Ubuntu's default APT repositories and start it right away:

$ sudo apt -y install postgresql
$ sudo systemctl enable --now postgresql

You can also install it from PostgreSQL's official APT repository, which you can read more about here.

Begin by creating a database user called dendrite. Type in a password when prompted:

$ sudo -u postgres createuser -P dendrite

Then, create a single database to run all Dendrite components:

$ sudo -u postgres createdb -O dendrite dendrite

It is possible to compartmentalise each component into its own database. This is useful if you want to host the databases on separate machines. For a single-user homeserver, a single database sharing the same system as the homeserver is sufficient and convenient.

Installing and configuring Dendrite

Now that you have Go installed and a database created, you may begin installing Dendrite. Clone the git repository to your /usr/local. Then, change your current working directory to the root of the repository and run the build script:

$ git clone https://github.com/matrix-org/dendrite -C /usr/local
$ cd /usr/local/dendrite
$ ./build.sh

Generate a private key to be used for signing messages between homeservers. This key must be saved in the repository's root directory. Losing it might impair federation between homeservers and you would have to create another homeserver with a different domain name, so it is important that you create a copy and back it up to somewhere safe like a password manager:

$ ./bin/generate-keys --private-key matrix_key.pem

Copy the configuration file template dendrite-config.yaml in the repository's root directory as dendrite.yaml:

$ cp dendrite-config.yaml dendrite.yaml

Edit this copied file with your preferred text editor. There are several variables you have to change:

Create a user which you will use to log in on a client. Replace <USER> with your desired username in the following command. You will be prompted to create a password for it:

$ ./bin/create-account -user <USER> -ask-pass

You will connect to your homeserver on a client using the hostname http://example.com:8008 for HTTP connections or https://example.com:8448 for HTTPS connections. The latter will be used, assuming you have TLS / SSL set up for your domain name. If you have a local firewall that denies incoming traffic, you need to open the ports 8008 and 8448. I use UFW and unblocked the ports with the following rules:

$ sudo ufw allow 8008
$ sudo ufw allow 8448
$ sudo ufw reload || sudo ufw enable

Now that you have fully configured your homeserver, it is time to run it for the first time. Create a systemd service:

$ sudo touch /etc/systemd/system/dendrite.service

Write the following to the file. Do not forget to replace example.com! I installed Let's Encrypt's SSL certificate for my domain via Certbot. If you used another way to install your domain's TLS / SSL certificate, modify the values of --tls-cert and --tls-key with the paths of the certificate and private key respectively:

# /etc/systemd/system/dendrite.service
[Unit]
Description="Dendrite: A second-generation Matrix homeserver written in Go."

[Service]
Type=simple
Restart=always
RestartSec=5
WorkingDirectory=/usr/local/dendrite
ExecStart=/usr/local/dendrite/bin/dendrite-monolith-server \
  --tls-cert /etc/letsencrypt/live/example.com/fullchain.pem \
  --tls-key /etc/letsencrypt/live/example.com/privkey.pem

[Install]
WantedBy=default.target

Start and enable the service at boot:

$ sudo systemctl enable --now dendrite.service

You should see no output, which indicates that no error has occured. To verify, run the following and check if the service is loaded and active:

$ sudo systemctl status dendrite.service

Congratulations, you now have a homeserver hot and running! Try logging in on a client to be really sure that everything is working well. See here for a list of clients to try out. My personal favourites are Element and FluffyChat.


Revisions


Have a comment on this blog post? Reach out to me via e-mail, Matrix or Pleroma.