September 7, 2022

Running Your Own Matrix Homeserver

This is an updated and improved version of an article published in February 2021.

Yesterday I played a bit with Synapse, the reference implementation of a Matrix homeserver. Matrix is a federated chat protocol that features end-to-end encryption and does not require a phone number. As the system is federated, everyone can run their own server but can still talk to users registered to other servers. So the situation is quite comparable to email.

Why I’m writing this article? Well, I believe that Matrix should be preferred over Signal or comparable applications due to its decentralized nature, as it does not expose your valuable phone number, and other reasons. Unfortunately, the biggest hurdle of installing a homeserver is finding a complete documentation, which the following one is - more or less.

Step 0: DNS

You need a domain that points to your vserver, Raspberry Pi, etc. Let’s call it domain.tld. In your DNS settings, also create a CNAME record called matrix.domain.tld. After completing the setup procedure, your Matrix IDs will have the following format: @name:domain.tld, not @name:matrix.domain.tld.

Step 1: Creating the Initial Configuration

Create a directory for you configuration mkdir -p /docker/matrix and cd /docker/matrix into it.

Here, run the following command:

docker run -it --rm \
    -v $(pwd)/data:/data \
    -e SYNAPSE_SERVER_NAME=domain.tld \
    matrixdotorg/synapse:latest generate

After this is complete, you’ll find some configuration files, keying material, and a TinySQL database in /docker/matrix/data. For me, the configuration file was okay and I didn’t need to change anything.

Step 2: docker-compose

The initial run command from above only created some config stuff. No Synapse process is running yet.

Create following docker-compose.yml file in /docker/matrix/

version: '3'

  container_name: synapse
  image: matrixdotorg/synapse:latest
  restart: always
      - ./data:/data
     - 8008:8008

… and run the beast with docker-compose up -d.

Step 3: Create Users

Execute docker exec -it synapse /bin/bash to spawn a shell in the running container. In the container execute register_new_matrix_user -c /data/homeserver.yaml Follow the instructions on the screen and enter a new user and password, and confirm the password. Repeat for other users.

Step 4: Reverse Proxy and SSL with NGINX

Now we reached the maybe most tricky bit: the proxy configuration.

I have setup my domain and server in a way that I can have a wildcard certificate for domain.tld, i.e., the certificate I have for domain.tld will also be valid for matrix.domain.tld. As setting up this feature depends a bit on your machine and your registrar, I cannot explain the necessary steps here.

The first nginx configuration file you have to create is the following:

server {

    server_name matrix.domain.tld;
    root /var/www/html/domain.tld;

    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    listen 8448 ssl http2 default_server;
    listen [::]:8448 ssl http2 default_server;

    location ~* ^(\/_matrix|\/_synapse\/client) {
        proxy_pass http://localhost:8008;
        proxy_set_header X-Forwarded-For $remote_addr;
        client_max_body_size 50M;

    ssl_certificate /etc/letsencrypt/live/domain.tld/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.tld/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

server {
  server_name matrix.domain.tld;

  listen 80;
  return 404;

  if ($host = matrix.domain.tld) {
      return 301 https://$host$request_uri;

I assume you already have a configuration file for domain.tld. To this file, add the following location:

location /.well-known/matrix/client {
    return 200 '{"m.homeserver": {"base_url": "https://matrix.domain.tld"}}';
    default_type application/json;
    add_header Access-Control-Allow-Origin *;

This statement creates some kind of redirect for Element (or other Matrix clients). You can now just enter domain.tld as your homeserver, and the client will automagically find out that matrix.domain.tld is your actual server.

Step 5: Testing

If everything worked according to plan, you should be able to do the following things:

  • Open and enter domain.tld. The tool should print out “success”, which means your server can talk to other servers. Nice.
  • Install Element on your local machine. Enter your homeserver’s URL domain.tld and the credentials. Element should connect, and you should be able to chat with other users.

Congratulations, you have set up a brand new federated and secure messaging server. Now good luck with convincing others to use this system as well :D

© ho1ger 2015 - 2022