Setting up an Unraid Matrix server

Intro

Hello there! This post is mainly about how I went about setting up a matrix server docker on “Unraid OS” and making it functional for a friend.

Disclaimer: During this setup I only use terminal access into the Unraid Server. Secondly, if you look at the webgui logs for the matrix server. It only displays the turnserver logs not the homeserver logs. You will need to terminal into server directly to access that file, or you can skip the need to terminal into server and use the samba shares instead. Lastly, this setup is not foolproof , there’s bound to be problems that can crop up. Some of them I am quite unceratin on how to resolve if they appear.

This may not be the case for others, but listed below are the files I edited to setup the Matrix docker.

/mnt/user/appdata/matrix/homserver.yaml # Main config that sets up the matrix server.

/mnt/user/appdata/swag/nginx/site-confs/default # Setting up the .well-known file to allow federation. 

Initial Setup

When adding the docker to Unraid the first thing you want to do when editing initial configs, is to set the server name. I advise this to be your top-level domain like solitem.net or matrix.org. The generated database will rely on the server name and you can’t change it once its created. To undo the change you would need to wipe the database and update the server name in homeserver.yaml, or just delete the docker including the data it generated.

I don’t know why, but for some reason the docker available through Unraid Community Apps (as of this writing), has some weird problems. Maybe it has something to do with how it’s maintained for Unraid. Either way, the problems as a whole aren’t particular massive. The core problem of the issues mainly stem from the default generated homeserver.yaml file. To be specific, two potential problems can occur.

Generated homeserver.yaml issues:

  • Bind address for port 8008
  • Default database filepath

Firstly, I’m going to tackle the bind address issue. Now this isn’t going to be a problem for everyone. By default it uses [‘0::1, 127.0.0.1’], which in theory should work, but the problem is that depending on the Unraid setup. There can be multiple network bridges at play and as such you would need to be aware of the ip addresses the docker is actually listening on. Now I initially took the lazyman approach and just did a 0.0.0.0 for the address, which may not be considered best practice. Eventually, I realized that the matrix server was listening on a specific network bridge. In particular, it was going off of what the Swag docker was using.

You may ask what Swag is. Essentially, its a reverse proxy docker that also handles encryption via Lets Encrypt. With Unraid I believe Swag creates a seperate network bridge that allows other dockers to communicate with each other. I at the time didn’t really notice this and took the catch all route for the matrix server. If you want the matrix server to work outside the local network, you should set the Matrix docker to the same network bridge as Swag.

 # This is what port 8008 should look like for it to function.
  #
  # Unsecure HTTP listener: for when matrix traffic passes through a reverse proxy
  # that unwraps TLS. 
  #
  # If you plan to use a reverse proxy, please see
  # https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.md.
  #
  - port: 8008
    tls: false
    type: http
    x_forwarded: true
    bind_addresses: ['172.20.0.2'] # Either set to 0.0.0.0 or ip address it uses for network bridge.

    resources:
      - names: [client, federation]
        compress: false

Moving onto the default database problem. This one is thankfully relative simple to fix. I believe by default Unraid sets the data mount path to /data. The generated homeserver.yaml only sets the database file path to “/homeserver.db” when in reality it needs to be “/data/homeserver.db”.

database:
  name: sqlite3
  args:
    database: /data/homeserver.db #ensure that /data is at the end or whatever is appropiate for the data mount in Unraid.

Now there’s a bit of a problem with using sqlite3 as the database. In a test environment its fine, but on a live enviornment performance may become a problem. From what I read, people recommend using postgresql. The reason I kept using sqlite3 in a live environment is due to a small userbase and that we weren’t going to be federating out into matrix networks like Matrix.org. My only concern is the longevity of the performance. Lastly, I believe it may be theoritically possible to use an alternate database, but that would require a different docker and figuring out to have each other communicate.

Once I fixed the two core problems with the generated config. I checked the homerserver log and it boots up as intended. At this point it should be functional aside a few things.

Federation and Registration

Now there’s mainly one crucial problem with the generated config. Although, it may not matter to those who don’t want it, but federation is by default disabled. Its not terribly difficult to enable it, but another potential problem occurs, delegation.

# Enabling Federation in homeserver.yaml

listeners:
 # Needs to be added below the listeners section of the config file.
 # Disabled by default. To enable it, uncomment the following.
 # Lastly, since the reverse proxy will be handling encryption set tls to false

  - port: 8448
    type: http
    tls: false

    resources:
      - names: [client, federation]

The issue with delegation is that if my server name is solitem.net, but traffic is only being received on matrix.solitem.net. Attempted federation is just going to fail due to not finding the server. Now I can skip the process of delegation by simply naming my server matrix.solitem.net, but its kind of like email when it displays my server name. When communicating outside of the matrix server. My user id would appear as rookblade:matrix.solitem.net. In the grand scheme of things, I could just accept that and not have to worry about delegation. However, I wanted the user id to appear as rookblade:solitem.net.

There’s two solutions in getting the matrix server properly delegated for federation, but I am only going to demonstrate one here. I don’t think either of them are that difficult to accomplish; although, it depends on overall experience. First option is to direct traffic to a “.well-known” file and the other option is to create a srv record from your dns provider. I am doing the .well-known file since its “relatively” considered to be the easiest option and that I can’t make a srv record due to limited access.

The method in which I redirected the servers to the appropiate path was through editing the default nginx config. In the default file I added 8 lines to the main server block. Both are where an inbound source requests a specific path, the web server will respond with a return 200 including an updated path to what they actually need. The differences between these two is that one was for clients and the other was for the server. I am unsure if the client was needed, but it was added in anyhow. From what I have seen, there’s more than one way of accomplishing this and it depending on the webserver it will look different.

# Make these changes inside the main server block.

# Not 100% certain that this is needed since testing demonstrated that federation
# works without it.
location /.well-known/matrix/client {
    return 200 "{'m.server': 'matrix.example.com:443'}";
    default_type application/json;
    add_header Access-Control-Allow-Origin *;
}

# At bare minimum this is required for the server to be able to federate.
location /.well-known/matrix/server {
    return 200 "{'m.server': 'matrix.example.com:443'}";
    default_type application/json;
    add_header Access-Control-Allow-Origin *;
}

# The steps to implement the .well-known files may be different on setup,
# but for nginx this should be relatively the same.

Once I implented the changes and restarted Swag I then used a site called: federationtester

The federation tester effectively checks to see any inherent issues when attempting to federate with the server. It will check to see if it can access the .well-known file or if it has a srv record, failing when neither are given. Once its able to get either one, it will confirm that both the ssl certificate and the server name are valid.

Side note: You want to use the server name with the federation tester. Otherwise, it will fail to connect or give an error that the server name does not match.

Once federation works, you can whitelist specifc servers to federate with. By default you will federate with everyone like matrix.org, but if you want to avoid that you can add one setting into homeserver.yaml, like so.

federation_domain_whitelist:
    - example.com
    - matrix.org
    - something.net

# I recommend enabling this if you want outside users to be able to access your public rooms.
# I believe they can still join your rooms via an invite if this option is disabled.
allow_public_rooms_over_federation: true

Enabling this will allow you to decide who can federate with you and whom your local users can federate with. You can still restrict access to your public rooms even, while being federated. Once that’s done, onwards to registration.

Unfortunately or fortunately, by default open registration is disabled. However, there’s not much in the way of solutions within Unraid’s Commnuity Apps. Either you allow open registration or you manually create the users, neither method is perfect. While I know its possible to due registration via tokens, problem is that it’s not on Unraid’s Community Apps and I am not particularly fluent with setting up dockers.

# Enable registration for new users.
enable_registration: true 

Conclusion

This covers the extent of what I did in setting up a matrix server via Unraid Community Apps. Honestly, there’s a lot that I can cover and will probably do a second part that goes into more detail of setting up a matrix server outside of Unraid. To be more exact, an alternate method that used Ansible to deploy the matrix server, but that also had tons of problems for me to get it functional. (matrix-docker-ansible-deploy)