Konstantin Nazarov

Setting up Dendrite Matrix server with Nix

Some time ago, I tried setting up a Matrix server on my personal domain with Element's hosted solution. They charge for it, and I'm generally OK with paying, but it still didn't work out well. Mostly because their version is for organizations that have many users, and thus still requires quite a bit of babysitting.

Enter Dendrite. This is a simple Matrix server, that has all required features for federation, and decent level of client compatibility. It doesn't scale quite as much as Synapse, but it should work fine for personal deployments.

You can reach out to me at https://matrix.to/#/@knazarov:knazarov.com. Gradually, I will reduce my usage of Telegram for communication, and replace it with Matrix.

For the adventurous souls, you can check out the snippet from my NixOS config that sets up Dendrite on the server side. It used dendrite.nix module, which is already available in NixOS. But as it doesn't have a good getting started article, configuring all the basic options may be hard for NixOS novices. So if for some reason you came here from search engines, feel free to peruse.

Note that nixpkgs tend to evolve, and it may stop working for you if you're reading this a long time after the post has been made. Refer to the latest configuration in git.knazarov.com/knazarov/nixos.

{ config, pkgs, ... }:

{
  imports =
    [
      ./hardware-configuration.nix
    ];

  boot.loader.grub.enable = true;
  networking.hostName = "knazarovcom";
  boot.loader.grub.device = "/dev/vda";

  users.users.knazarov = {
    isNormalUser = true;
    extraGroups = [ "wheel" ];
  };

  environment.systemPackages = with pkgs; [
    sops
  ];

  services.nginx = {
    enable = true;
    virtualHosts = {
      "knazarov.com" = {
        enableACME = true;
        forceSSL = true;
        # Contains this site
        root = "${pkgs.knazarovcom}/srv/knazarov.com";

        locations."/.well-known/matrix/server" = {
          extraConfig = ''
            default_type application/json;
            return 200 '{ "m.server": "matrix.knazarov.com:443" }';
          '';
        };
        locations."/.well-known/matrix/client" = {
          extraConfig = ''
            default_type application/json;
            return 200 '{ "m.homeserver": { "base_url": "https://matrix.knazarov.com" } }';
            add_header "Access-Control-Allow-Origin" *;
          '';
        };
      };
      "matrix.knazarov.com" = {
        enableACME = true;
        forceSSL = true;
        locations."/_matrix" = {
          proxyPass = "http://127.0.0.1:8008";
        };
      };
    };
  };
  security.acme.acceptTerms = true;
  security.acme.certs = {
    "knazarov.com".email = "mail@knazarov.com";
    "matrix.knazarov.com".email = "mail@knazarov.com";
  };

  services.dendrite = {
    enable = true;
    environmentFile = config.sops.secrets.matrix_registration_secret.path;
    settings = {
      global = {
        server_name = "knazarov.com";
        private_key = config.sops.secrets.matrix_key.path;
      };
      client_api.registration_shared_secret = "$REGISTRATION_SHARED_SECRET";
    };
  };

  systemd.services.dendrite = {
    serviceConfig.SupplementaryGroups = [ config.users.groups.keys.name ];
  };

  sops.defaultSopsFile = ./secrets.yaml;
  sops.secrets = {
    example_key = {};
    matrix_key = {
      mode = "0440";
      group = config.users.groups.keys.name;
    };
    matrix_registration_secret = {
      mode = "0440";
      group = config.users.groups.keys.name;
    };
  };

  networking.firewall.allowedTCPPorts = [ 80 443 ];

  system.stateVersion = "23.05";
}