Declarative, Decentralised, and Secure communication via Matrix, Jitsi, & NixOS

It has been one of my strong pursuit to figure out a properly encrypted and decentralised communication setup. Matrix’s claim to provide that is pretty strong against other alternatives. I’ve been using Riot, the most feature rich Matrix client, but mainly as an IRC bouncer.

Just the other day I read this awesome guide on Running your own secure communication service with Matrix and Jitsi and realised it’s not that difficult nowadays to setup all the pieces of the puzzle.

And so if I want to follow those steps shown in the above article but rather do it in NixOS - another project I am really excited about - how easy/difficult it would be ?

Well apart from minor tweaks, it was pretty straightforward and I would argue more simpler than how its done for a Debian based system. Following is the rundown of how I made the relevant steps work for my setup. I am not going to repeat all the nice technical explanations of above article. So anyone following should first read the above article.

Before we begin, I would also like to address one difference from the basic premise of the above article - Unlike in the above article the main domain ( is already pointing to an existing blog hosted from a Github repository. I think this might be a case for many like me who already have a domain name serving a static blog (like this one) but want to setup Matrix via the subdomains.

So lets begin,


With NixOS every step is declarative and hence we don’t have to worry about the mechanical rigor as is the case with most of the steps in the original article. What it entails for us broadly are these 3 steps (apart from some additional stuff)

  • Update /etc/nixos/configuration.nix
  • Run sudo nixos-rebuild switch
  • Done!

NixOS made following the original article really easy as I could make mistakes and not worry because I could rollback and start again. Let that sink in as I think that in itself is mind-blowing.


Let’s use the same domain name - - as used in the original article for our explanations. As mentioned above, we have one change here that the root domain already is redirecting to the static blog hosted in Github Pages. So we need to setup A records for the following subdomains:

  • (for Synapse)
  • (for Riot/Web)
  • (for Jitsi)
  • (for coturn based TURN server that in original article is taken care by jitsi-meet installer)

Its important for all these to setup in the DNS before we run our configurations so that LetsEncrypt challenge flow works fine or else we might face errors.


We need to ensure that following ports are open in the NixOS firewall for all the configurations to work fine

Nginx with LetsEncrypt

The above setups Nginx along with LetsEncrypt. If it’s required (like in the original article) to setup main domain as a virtual host then make an additional entry inside virtualHosts


Unlike in the original article we will do what’s recommended by Synapse team and configure PostgreSQL. In the current NixOS configuration for services.matrix-synapse it might be configuring PostgreSQL automatically based on the database_type setting but that will change in the new versions of NixOS where PostgreSQL setups need to happen separately. Here’s how we will do it for Synapse

Federation setting

Original article’s description for how to configure so that Matrix could find one’s server is nice. I am just leveraging that with one additional setting required when there is already a static blog running via Github Pages pointing to base domain:

  • create a file having this path .well-known/matrix/server in the top level of the Github repository serving the static site having the following content

  • Github Pages via Jekyll exclude dotfiles/dotdirs from serving, so we need to explicitly include them by creating _config.yml at the top level of the Github repository having the following content


riot-web is a package already available within Nix packages which will take care of getting the right release package along with signature verification. So we can safely add the same to systemPackages like this

However, we need to let know Nix of the additional configuration required once riot-web is setup. That we could do via overlays. This is another advantage of declarative package management where we state in clear terms the state of our system which also help us revert in case unexpected happens.


Finally we head towards our last leg of configurations - setting up jitsi-meet. Unfortunately, current master and stable channels of Nix packages lack jitsi-meet and other supporting packages required to setup Jitsi. However, it will change pretty soon thanks to the awesome work from user mmilata in this pull request. It’s feature complete and might come as part of the next Nix release (20.03). However, we can already use the same via NUR.

The above import add only the new service to setup jitsi-meet without any other OS specific stuff. And the configuration for the new service would auto-magically setup the related software (like videobridge & jicofo) to have a functioning Jitsi. We can then add a new virtual host to the Nginx configuration with the Jitsi hostName mentioned below (shown above in Nginx Section)

TURN server

jitsi-meet Debian package takes care of setting up TURN server but in our case we need to take care of it ourselves. I followed the instructions mentioned in this documentation. Nix already has a service configuration to setup coturn. Once set up, we will integrate the relevant parts to Synapse (which we have already done above).

static-auth-secret generated above is via the tool pwgen which in Nix we could do like this


Once we have all the above configuration in place, now is the time to finally run them all. This will take time to get all the necessary stuff downloaded and setup.

However, once its done you can launch Riot/Web, register a new user, create a room with someone else from the same MatrixVerse, have end-to-end encrypted conversation, and also start having a video call via Jitsi. All that infrastructure now being 100% owned by you with a protocol enforcing privacy and decentralisation. And finally every piece here is fully open-source.

In these times of covert and overt surveillance its imperative to take matters at your hand. And when right tools make it simple to set them up and easy to reason their behavior, it’s a nice feeling.