summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2205.section.xml110
-rw-r--r--nixos/doc/manual/release-notes/rl-2205.section.md89
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/services/matrix/matrix-synapse-log_config.yaml (renamed from nixos/modules/services/misc/matrix-synapse-log_config.yaml)0
-rw-r--r--nixos/modules/services/matrix/matrix-synapse.nix773
-rw-r--r--nixos/modules/services/matrix/matrix-synapse.xml (renamed from nixos/modules/services/misc/matrix-synapse.xml)35
-rw-r--r--nixos/modules/services/misc/matrix-synapse.nix844
-rw-r--r--nixos/tests/matrix-appservice-irc.nix46
-rw-r--r--nixos/tests/matrix-synapse.nix64
-rw-r--r--nixos/tests/matrix/mjolnir.nix43
-rw-r--r--nixos/tests/matrix/pantalaimon.nix29
11 files changed, 1112 insertions, 923 deletions
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
index 2d8279725ff..4f4a5a3394e 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
@@ -390,6 +390,116 @@
       </listitem>
       <listitem>
         <para>
+          The <literal>matrix-synapse</literal> service
+          (<literal>services.matrix-synapse</literal>) has been
+          converted to use the <literal>settings</literal> option
+          defined in RFC42. This means that options that are part of
+          your <literal>homeserver.yaml</literal> configuration, and
+          that were specified at the top-level of the module
+          (<literal>services.matrix-synapse</literal>) now need to be
+          moved into
+          <literal>services.matrix-synapse.settings</literal>. And while
+          not all options you may use are defined in there, they are
+          still supported, because you can set arbitrary values in this
+          freeform type.
+        </para>
+        <para>
+          An example to make the required migration clearer:
+        </para>
+        <para>
+          Before:
+        </para>
+        <programlisting language="bash">
+{
+  services.matrix-synapse = {
+    enable = true;
+
+    server_name = &quot;example.com&quot;;
+    public_baseurl = &quot;https://example.com:8448&quot;;
+
+    enable_registration = false;
+    registration_shared_secret = &quot;xohshaeyui8jic7uutuDogahkee3aehuaf6ei3Xouz4iicie5thie6nohNahceut&quot;;
+    macaroon_secret_key = &quot;xoo8eder9seivukaiPh1cheikohquuw8Yooreid0The4aifahth3Ou0aiShaiz4l&quot;;
+
+    tls_certificate_path = &quot;/var/lib/acme/example.com/fullchain.pem&quot;;
+    tls_certificate_path = &quot;/var/lib/acme/example.com/fullchain.pem&quot;;
+
+    listeners = [ {
+      port = 8448;
+      bind_address = &quot;&quot;;
+      type = &quot;http&quot;;
+      tls = true;
+      resources = [ {
+        names = [ &quot;client&quot; ];
+        compress = true;
+      } {
+        names = [ &quot;federation&quot; ];
+        compress = false;
+      } ];
+    } ];
+
+  };
+}
+</programlisting>
+        <para>
+          After:
+        </para>
+        <programlisting language="bash">
+{
+  services.matrix-synapse = {
+    enable = true;
+
+    # this attribute set holds all values that go into your homeserver.yaml configuration
+    # See https://github.com/matrix-org/synapse/blob/develop/docs/sample_config.yaml for
+    # possible values.
+    settings = {
+      server_name = &quot;example.com&quot;;
+      public_baseurl = &quot;https://example.com:8448&quot;;
+
+      enable_registration = false;
+      # pass `registration_shared_secret` and `macaroon_secret_key` via `extraConfigFiles` instead
+
+      tls_certificate_path = &quot;/var/lib/acme/example.com/fullchain.pem&quot;;
+      tls_certificate_path = &quot;/var/lib/acme/example.com/fullchain.pem&quot;;
+
+      listeners = [ {
+        port = 8448;
+        bind_address = [
+          &quot;::&quot;
+          &quot;0.0.0.0&quot;
+        ];
+        type = &quot;http&quot;;
+        tls = true;
+        resources = [ {
+          names = [ &quot;client&quot; ];
+          compress = true;
+        } {
+          names = [ &quot;federation&quot; ];
+          compress = false;
+        } ];
+      } ];
+    };
+
+    extraConfigFiles = [
+      /run/keys/matrix-synapse/secrets.yaml
+    ];
+  };
+}
+</programlisting>
+        <para>
+          The secrets in your original config should be migrated into a
+          YAML file that is included via
+          <literal>extraConfigFiles</literal>.
+        </para>
+        <para>
+          Additionally a few option defaults have been synced up with
+          upstream default values, for example the
+          <literal>max_upload_size</literal> grew from
+          <literal>10M</literal> to <literal>50M</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           The MoinMoin wiki engine
           (<literal>services.moinmoin</literal>) has been removed,
           because Python 2 is being retired from nixpkgs.
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 51d7f009606..c4281561f16 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -128,6 +128,95 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - The `mailpile` email webclient (`services.mailpile`) has been removed due to its reliance on python2.
 
+- The `matrix-synapse` service (`services.matrix-synapse`) has been converted to use the `settings` option defined in RFC42.
+  This means that options that are part of your `homeserver.yaml` configuration, and that were specified at the top-level of the
+  module (`services.matrix-synapse`) now need to be moved into `services.matrix-synapse.settings`. And while not all options you
+  may use are defined in there, they are still supported, because you can set arbitrary values in this freeform type.
+
+  An example to make the required migration clearer:
+
+  Before:
+  ```nix
+  {
+    services.matrix-synapse = {
+      enable = true;
+
+      server_name = "example.com";
+      public_baseurl = "https://example.com:8448";
+
+      enable_registration = false;
+      registration_shared_secret = "xohshaeyui8jic7uutuDogahkee3aehuaf6ei3Xouz4iicie5thie6nohNahceut";
+      macaroon_secret_key = "xoo8eder9seivukaiPh1cheikohquuw8Yooreid0The4aifahth3Ou0aiShaiz4l";
+
+      tls_certificate_path = "/var/lib/acme/example.com/fullchain.pem";
+      tls_certificate_path = "/var/lib/acme/example.com/fullchain.pem";
+
+      listeners = [ {
+        port = 8448;
+        bind_address = "";
+        type = "http";
+        tls = true;
+        resources = [ {
+          names = [ "client" ];
+          compress = true;
+        } {
+          names = [ "federation" ];
+          compress = false;
+        } ];
+      } ];
+
+    };
+  }
+  ```
+
+  After:
+  ```nix
+  {
+    services.matrix-synapse = {
+      enable = true;
+
+      # this attribute set holds all values that go into your homeserver.yaml configuration
+      # See https://github.com/matrix-org/synapse/blob/develop/docs/sample_config.yaml for
+      # possible values.
+      settings = {
+        server_name = "example.com";
+        public_baseurl = "https://example.com:8448";
+
+        enable_registration = false;
+        # pass `registration_shared_secret` and `macaroon_secret_key` via `extraConfigFiles` instead
+
+        tls_certificate_path = "/var/lib/acme/example.com/fullchain.pem";
+        tls_certificate_path = "/var/lib/acme/example.com/fullchain.pem";
+
+        listeners = [ {
+          port = 8448;
+          bind_address = [
+            "::"
+            "0.0.0.0"
+          ];
+          type = "http";
+          tls = true;
+          resources = [ {
+            names = [ "client" ];
+            compress = true;
+          } {
+            names = [ "federation" ];
+            compress = false;
+          } ];
+        } ];
+      };
+
+      extraConfigFiles = [
+        /run/keys/matrix-synapse/secrets.yaml
+      ];
+    };
+  }
+  ```
+
+  The secrets in your original config should be migrated into a YAML file that is included via `extraConfigFiles`.
+
+  Additionally a few option defaults have been synced up with upstream default values, for example the `max_upload_size` grew from `10M` to `50M`.
+
 - The MoinMoin wiki engine (`services.moinmoin`) has been removed, because Python 2 is being retired from nixpkgs.
 
 - The `wafHook` hook now honors `NIX_BUILD_CORES` when `enableParallelBuilding` is not set explicitly. Packages can restore the old behaviour by setting `enableParallelBuilding=false`.
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 7bce1119d73..1d2faddfff2 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -499,6 +499,7 @@
   ./services/mail/roundcube.nix
   ./services/mail/sympa.nix
   ./services/mail/nullmailer.nix
+  ./services/matrix/matrix-synapse.nix
   ./services/matrix/mjolnir.nix
   ./services/matrix/pantalaimon.nix
   ./services/misc/ananicy.nix
@@ -565,7 +566,6 @@
   ./services/misc/matrix-appservice-discord.nix
   ./services/misc/matrix-appservice-irc.nix
   ./services/misc/matrix-conduit.nix
-  ./services/misc/matrix-synapse.nix
   ./services/misc/mautrix-facebook.nix
   ./services/misc/mautrix-telegram.nix
   ./services/misc/mbpfan.nix
diff --git a/nixos/modules/services/misc/matrix-synapse-log_config.yaml b/nixos/modules/services/matrix/matrix-synapse-log_config.yaml
index d85bdd1208f..d85bdd1208f 100644
--- a/nixos/modules/services/misc/matrix-synapse-log_config.yaml
+++ b/nixos/modules/services/matrix/matrix-synapse-log_config.yaml
diff --git a/nixos/modules/services/matrix/matrix-synapse.nix b/nixos/modules/services/matrix/matrix-synapse.nix
new file mode 100644
index 00000000000..c4d14dbd547
--- /dev/null
+++ b/nixos/modules/services/matrix/matrix-synapse.nix
@@ -0,0 +1,773 @@
+{ config, lib, options, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.matrix-synapse;
+  format = pkgs.formats.yaml {};
+
+  # remove null values from the final configuration
+  finalSettings = lib.filterAttrsRecursive (_: v: v != null) cfg.settings;
+  configFile = format.generate "homeserver.yaml" finalSettings;
+  logConfigFile = format.generate "log_config.yaml" cfg.logConfig;
+
+  pluginsEnv = cfg.package.python.buildEnv.override {
+    extraLibs = cfg.plugins;
+  };
+
+  usePostgresql = cfg.settings.database.name == "psycopg2";
+  hasLocalPostgresDB = let args = cfg.settings.database.args; in
+    usePostgresql && (!(args ? host) || (elem args.host [ "localhost" "127.0.0.1" "::1" ]));
+
+  registerNewMatrixUser =
+    let
+      isIpv6 = x: lib.length (lib.splitString ":" x) > 1;
+      listener =
+        lib.findFirst (
+          listener: lib.any (
+            resource: lib.any (
+              name: name == "client"
+            ) resource.names
+          ) listener.resources
+        ) (lib.last cfg.settings.listeners) cfg.settings.listeners;
+        # FIXME: Handle cases with missing client listener properly,
+        # don't rely on lib.last, this will not work.
+
+      # add a tail, so that without any bind_addresses we still have a useable address
+      bindAddress = head (listener.bind_addresses ++ [ "127.0.0.1" ]);
+      listenerProtocol = if listener.tls
+        then "https"
+        else "http";
+    in
+    pkgs.writeShellScriptBin "matrix-synapse-register_new_matrix_user" ''
+      exec ${cfg.package}/bin/register_new_matrix_user \
+        $@ \
+        ${lib.concatMapStringsSep " " (x: "-c ${x}") ([ configFile ] ++ cfg.extraConfigFiles)} \
+        "${listenerProtocol}://${
+          if (isIpv6 bindAddress) then
+            "[${bindAddress}]"
+          else
+            "${bindAddress}"
+        }:${builtins.toString listener.port}/"
+    '';
+in {
+
+  imports = [
+
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "trusted_third_party_id_servers" ] ''
+      The `trusted_third_party_id_servers` option as been removed in `matrix-synapse` v1.4.0
+      as the behavior is now obsolete.
+    '')
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "create_local_database" ] ''
+      Database configuration must be done manually. An exemplary setup is demonstrated in
+      <nixpkgs/nixos/tests/matrix-synapse.nix>
+    '')
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "web_client" ] "")
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "room_invite_state_types" ] ''
+      You may add additional event types via
+      `services.matrix-synapse.room_prejoin_state.additional_event_types` and
+      disable the default events via
+      `services.matrix-synapse.room_prejoin_state.disable_default_event_types`.
+    '')
+
+    # options that don't exist in synapse anymore
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "bind_host" ] "Use listener settings instead." )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "bind_port" ] "Use listener settings instead." )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "expire_access_tokens" ] "" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "no_tls" ] "It is no longer supported by synapse." )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "tls_dh_param_path" ] "It was removed from synapse." )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "unsecure_port" ] "Use settings.listeners instead." )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "user_creation_max_duration" ] "It is no longer supported by synapse." )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "verbose" ] "Use a log config instead." )
+
+    # options that were moved into rfc42 style settigns
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "app_service_config_files" ] "Use settings.app_service_config_Files instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "database_args" ] "Use settings.database.args instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "database_name" ] "Use settings.database.args.database instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "database_type" ] "Use settings.database.name instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "database_user" ] "Use settings.database.args.user instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "dynamic_thumbnails" ] "Use settings.dynamic_thumbnails instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "enable_metrics" ] "Use settings.enable_metrics instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "enable_registration" ] "Use settings.enable_registration instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "extraConfig" ] "Use settings instead." )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "listeners" ] "Use settings.listeners instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "logConfig" ] "Use settings.log_config instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "max_image_pixels" ] "Use settings.max_image_pixels instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "max_upload_size" ] "Use settings.max_upload_size instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "presence" "enabled" ] "Use settings.presence.enabled instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "public_baseurl" ] "Use settings.public_baseurl instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "report_stats" ] "Use settings.report_stats instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "server_name" ] "Use settings.server_name instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "servers" ] "Use settings.trusted_key_servers instead." )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "tls_certificate_path" ] "Use settings.tls_certificate_path instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "tls_private_key_path" ] "Use settings.tls_private_key_path instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_shared_secret" ] "Use settings.turn_shared_secret instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_uris" ] "Use settings.turn_uris instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_user_lifetime" ] "Use settings.turn_user_lifetime instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_enabled" ] "Use settings.url_preview_enabled instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_ip_range_blacklist" ] "Use settings.url_preview_ip_range_blacklist instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_ip_range_whitelist" ] "Use settings.url_preview_ip_range_whitelist instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_url_blacklist" ] "Use settings.url_preview_url_blacklist instead" )
+
+    # options that are too specific to mention them explicitly in settings
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "account_threepid_delegates" "email" ] "Use settings.account_threepid_delegates.email instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "account_threepid_delegates" "msisdn" ] "Use settings.account_threepid_delegates.msisdn instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "allow_guest_access" ] "Use settings.allow_guest_access instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "bcrypt_rounds" ] "Use settings.bcrypt_rounds instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "enable_registration_captcha" ] "Use settings.enable_registration_captcha instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "event_cache_size" ] "Use settings.event_cache_size instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_concurrent" ] "Use settings.rc_federation.concurrent instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_reject_limit" ] "Use settings.rc_federation.reject_limit instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_sleep_delay" ] "Use settings.rc_federation.sleep_delay instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_sleep_limit" ] "Use settings.rc_federation.sleep_limit instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_window_size" ] "Use settings.rc_federation.window_size instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "key_refresh_interval" ] "Use settings.key_refresh_interval instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "rc_messages_burst_count" ] "Use settings.rc_messages.burst_count instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "rc_messages_per_second" ] "Use settings.rc_messages.per_second instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "recaptcha_private_key" ] "Use settings.recaptcha_private_key instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "recaptcha_public_key" ] "Use settings.recaptcha_public_key instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "redaction_retention_period" ] "Use settings.redaction_retention_period instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "room_prejoin_state" "additional_event_types" ] "Use settings.room_prejoin_state.additional_event_types instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "room_prejoin_state" "disable_default_event_types" ] "Use settings.room_prejoin-state.disable_default_event_types instead" )
+
+    # Options that should be passed via extraConfigFiles, so they are not persisted into the nix store
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "macaroon_secret_key" ] "Pass this value via extraConfigFiles instead" )
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "registration_shared_secret" ] "Pass this value via extraConfigFiles instead" )
+
+  ];
+
+  options = {
+    services.matrix-synapse = {
+      enable = mkEnableOption "matrix.org synapse";
+
+      configFile = mkOption {
+        type = types.str;
+        readOnly = true;
+        description = ''
+          Path to the configuration file on the target system. Useful to configure e.g. workers
+          that also need this.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.matrix-synapse;
+        defaultText = literalExpression "pkgs.matrix-synapse";
+        description = ''
+          Overridable attribute of the matrix synapse server package to use.
+        '';
+      };
+
+      plugins = mkOption {
+        type = types.listOf types.package;
+        default = [ ];
+        example = literalExpression ''
+          with config.services.matrix-synapse.package.plugins; [
+            matrix-synapse-ldap3
+            matrix-synapse-pam
+          ];
+        '';
+        description = ''
+          List of additional Matrix plugins to make available.
+        '';
+      };
+
+      withJemalloc = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to preload jemalloc to reduce memory fragmentation and overall usage.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.str;
+        default = "/var/lib/matrix-synapse";
+        description = ''
+          The directory where matrix-synapse stores its stateful data such as
+          certificates, media and uploads.
+        '';
+      };
+
+      settings = mkOption {
+        default = {};
+        description = ''
+          The primary synapse configuration. See the
+          <link xlink:href="https://github.com/matrix-org/synapse/blob/v${cfg.package.version}/docs/sample_config.yaml">sample configuration</link>
+          for possible values.
+
+          Secrets should be passed in by using the <literal>extraConfigFiles</literal> option.
+        '';
+        type = with types; submodule {
+          freeformType = format.type;
+          options = {
+            # This is a reduced set of popular options and defaults
+            # Do not add every available option here, they can be specified
+            # by the user at their own discretion. This is a freeform type!
+
+            server_name = mkOption {
+              type = types.str;
+              example = "example.com";
+              default = config.networking.hostName;
+              defaultText = literalExpression "config.networking.hostName";
+              description = ''
+                The domain name of the server, with optional explicit port.
+                This is used by remote servers to look up the server address.
+                This is also the last part of your UserID.
+
+                The server_name cannot be changed later so it is important to configure this correctly before you start Synapse.
+              '';
+            };
+
+            enable_registration = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Enable registration for new users.
+              '';
+            };
+
+            registration_shared_secret = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              description = ''
+                If set, allows registration by anyone who also has the shared
+                secret, even if registration is otherwise disabled.
+
+                Secrets should be passed in via <literal>extraConfigFiles</literal>!
+              '';
+            };
+
+            macaroon_secret_key = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              description = ''
+                Secret key for authentication tokens. If none is specified,
+                the registration_shared_secret is used, if one is given; otherwise,
+                a secret key is derived from the signing key.
+
+                Secrets should be passed in via <literal>extraConfigFiles</literal>!
+              '';
+            };
+
+            enable_metrics = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Enable collection and rendering of performance metrics
+              '';
+            };
+
+            report_stats = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Whether or not to report anonymized homeserver usage statistics.
+              '';
+            };
+
+            signing_key_path = mkOption {
+              type = types.path;
+              default = "${cfg.dataDir}/homeserver.signing.key";
+              description = ''
+                Path to the signing key to sign messages with.
+              '';
+            };
+
+            pid_file = mkOption {
+              type = types.path;
+              default = "/run/matrix-synapse.pid";
+              readOnly = true;
+              description = ''
+                The file to store the PID in.
+              '';
+            };
+
+            log_config = mkOption {
+              type = types.path;
+              default = ./matrix-synapse-log_config.yaml;
+              description = ''
+                The file that holds the logging configuration.
+              '';
+            };
+
+            media_store_path = mkOption {
+              type = types.path;
+              default = if lib.versionAtLeast config.system.stateVersion "22.05"
+                then "${cfg.dataDir}/media_store"
+                else "${cfg.dataDir}/media";
+              description = ''
+                Directory where uploaded images and attachments are stored.
+              '';
+            };
+
+            public_baseurl = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "https://example.com:8448/";
+              description = ''
+                The public-facing base URL for the client API (not including _matrix/...)
+              '';
+            };
+
+            tls_certificate_path = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "/var/lib/acme/example.com/fullchain.pem";
+              description = ''
+                PEM encoded X509 certificate for TLS.
+                You can replace the self-signed certificate that synapse
+                autogenerates on launch with your own SSL certificate + key pair
+                if you like.  Any required intermediary certificates can be
+                appended after the primary certificate in hierarchical order.
+              '';
+            };
+
+            tls_private_key_path = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "/var/lib/acme/example.com/key.pem";
+              description = ''
+                PEM encoded private key for TLS. Specify null if synapse is not
+                speaking TLS directly.
+              '';
+            };
+
+            presence.enabled = mkOption {
+              type = types.bool;
+              default = true;
+              example = false;
+              description = ''
+                Whether to enable presence tracking.
+
+                Presence tracking allows users to see the state (e.g online/offline)
+                of other local and remote users.
+              '';
+            };
+
+            listeners = mkOption {
+              type = types.listOf (types.submodule {
+                options = {
+                  port = mkOption {
+                    type = types.port;
+                    example = 8448;
+                    description = ''
+                      The port to listen for HTTP(S) requests on.
+                    '';
+                  };
+
+                  bind_addresses = mkOption {
+                    type = types.listOf types.str;
+                    default = [
+                      "::1"
+                      "127.0.0.1"
+                    ];
+                    example = literalExpression ''
+                    [
+                      "::"
+                      "0.0.0.0"
+                    ]
+                    '';
+                    description = ''
+                     IP addresses to bind the listener to.
+                    '';
+                  };
+
+                  type = mkOption {
+                    type = types.enum [
+                      "http"
+                      "manhole"
+                      "metrics"
+                      "replication"
+                    ];
+                    default = "http";
+                    example = "metrics";
+                    description = ''
+                      The type of the listener, usually http.
+                    '';
+                  };
+
+                  tls = mkOption {
+                    type = types.bool;
+                    default = true;
+                    example = false;
+                    description = ''
+                      Whether to enable TLS on the listener socket.
+                    '';
+                  };
+
+                  x_forwarded = mkOption {
+                    type = types.bool;
+                    default = false;
+                    example = true;
+                    description = ''
+                      Use the X-Forwarded-For (XFF) header as the client IP and not the
+                      actual client IP.
+                    '';
+                  };
+
+                  resources = mkOption {
+                    type = types.listOf (types.submodule {
+                      options = {
+                        names = mkOption {
+                          type = types.listOf (types.enum [
+                            "client"
+                            "consent"
+                            "federation"
+                            "keys"
+                            "media"
+                            "metrics"
+                            "openid"
+                            "replication"
+                            "static"
+                          ]);
+                          description = ''
+                            List of resources to host on this listener.
+                          '';
+                          example = [
+                            "client"
+                          ];
+                        };
+                        compress = mkOption {
+                          type = types.bool;
+                          description = ''
+                            Should synapse compress HTTP responses to clients that support it?
+                            This should be disabled if running synapse behind a load balancer
+                            that can do automatic compression.
+                          '';
+                        };
+                      };
+                    });
+                    description = ''
+                      List of HTTP resources to serve on this listener.
+                    '';
+                  };
+                };
+              });
+              default = [ {
+                port = 8008;
+                bind_addresses = [ "127.0.0.1" ];
+                type = "http";
+                tls = false;
+                x_forwarded = true;
+                resources = [ {
+                  names = [ "client" ];
+                  compress = true;
+                } {
+                  names = [ "federation" ];
+                  compress = false;
+                } ];
+              } ];
+              description = ''
+                List of ports that Synapse should listen on, their purpose and their configuration.
+              '';
+            };
+
+            database.name = mkOption {
+              type = types.enum [
+                "sqlite3"
+                "psycopg2"
+              ];
+              default = if versionAtLeast config.system.stateVersion "18.03"
+                then "psycopg2"
+                else "sqlite3";
+               defaultText = literalExpression ''
+                if versionAtLeast config.system.stateVersion "18.03"
+                then "psycopg2"
+                else "sqlite3"
+              '';
+              description = ''
+                The database engine name. Can be sqlite3 or psycopg2.
+              '';
+            };
+
+            database.args.database = mkOption {
+              type = types.str;
+              default = {
+                sqlite3 = "${cfg.dataDir}/homeserver.db";
+                psycopg2 = "matrix-synapse";
+              }.${cfg.settings.database.name};
+              defaultText = literalExpression ''
+              {
+                sqlite3 = "''${${options.services.matrix-synapse.dataDir}}/homeserver.db";
+                psycopg2 = "matrix-synapse";
+              }.''${${options.services.matrix-synapse.settings}.database.name};
+              '';
+              description = ''
+                Name of the database when using the psycopg2 backend,
+                path to the database location when using sqlite3.
+              '';
+            };
+
+            database.args.user = mkOption {
+              type = types.nullOr types.str;
+              default = {
+                sqlite3 = null;
+                psycopg2 = "matrix-synapse";
+              }.${cfg.settings.database.name};
+              description = ''
+                Username to connect with psycopg2, set to null
+                when using sqlite3.
+              '';
+            };
+
+            url_preview_enabled = mkOption {
+              type = types.bool;
+              default = true;
+              example = false;
+              description = ''
+                Is the preview URL API enabled?  If enabled, you *must* specify an
+                explicit url_preview_ip_range_blacklist of IPs that the spider is
+                denied from accessing.
+              '';
+            };
+
+            url_preview_ip_range_blacklist = mkOption {
+              type = types.listOf types.str;
+              default = [
+                "10.0.0.0/8"
+                "100.64.0.0/10"
+                "127.0.0.0/8"
+                "169.254.0.0/16"
+                "172.16.0.0/12"
+                "192.0.0.0/24"
+                "192.0.2.0/24"
+                "192.168.0.0/16"
+                "192.88.99.0/24"
+                "198.18.0.0/15"
+                "198.51.100.0/24"
+                "2001:db8::/32"
+                "203.0.113.0/24"
+                "224.0.0.0/4"
+                "::1/128"
+                "fc00::/7"
+                "fe80::/10"
+                "fec0::/10"
+                "ff00::/8"
+              ];
+              description = ''
+                List of IP address CIDR ranges that the URL preview spider is denied
+                from accessing.
+              '';
+            };
+
+            url_preview_ip_range_whitelist = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              description = ''
+                List of IP address CIDR ranges that the URL preview spider is allowed
+                to access even if they are specified in url_preview_ip_range_blacklist.
+              '';
+            };
+
+            url_preview_url_blacklist = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              description = ''
+                Optional list of URL matches that the URL preview spider is
+                denied from accessing.
+              '';
+            };
+
+            max_upload_size = mkOption {
+              type = types.str;
+              default = "50M";
+              example = "100M";
+              description = ''
+                The largest allowed upload size in bytes
+              '';
+            };
+
+            max_image_pixels = mkOption {
+              type = types.str;
+              default = "32M";
+              example = "64M";
+              description = ''
+                Maximum number of pixels that will be thumbnailed
+              '';
+            };
+
+            dynamic_thumbnails = mkOption {
+              type = types.bool;
+              default = false;
+              example = true;
+              description = ''
+                Whether to generate new thumbnails on the fly to precisely match
+                the resolution requested by the client. If true then whenever
+                a new resolution is requested by the client the server will
+                generate a new thumbnail. If false the server will pick a thumbnail
+                from a precalculated list.
+              '';
+            };
+
+            turn_uris = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = [
+                "turn:turn.example.com:3487?transport=udp"
+                "turn:turn.example.com:3487?transport=tcp"
+                "turns:turn.example.com:5349?transport=udp"
+                "turns:turn.example.com:5349?transport=tcp"
+              ];
+              description = ''
+                The public URIs of the TURN server to give to clients
+              '';
+            };
+            turn_shared_secret = mkOption {
+              type = types.str;
+              default = "";
+              example = literalExpression ''
+                config.services.coturn.static-auth-secret
+              '';
+              description = ''
+                The shared secret used to compute passwords for the TURN server.
+
+                Secrets should be passed in via <literal>extraConfigFiles</literal>!
+              '';
+            };
+
+            trusted_key_servers = mkOption {
+              type = types.listOf (types.submodule {
+                options = {
+                  server_name = mkOption {
+                    type = types.str;
+                    example = "matrix.org";
+                    description = ''
+                      Hostname of the trusted server.
+                    '';
+                  };
+
+                  verify_keys = mkOption {
+                    type = types.nullOr (types.attrsOf types.str);
+                    default = null;
+                    example = literalExpression ''
+                      {
+                        "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
+                      }
+                    '';
+                    description = ''
+                      Attribute set from key id to base64 encoded public key.
+
+                      If specified synapse will check that the response is signed
+                      by at least one of the given keys.
+                    '';
+                  };
+                };
+              });
+              default = [ {
+                server_name = "matrix.org";
+                verify_keys = {
+                  "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
+                };
+              } ];
+              description = ''
+                The trusted servers to download signing keys from.
+              '';
+            };
+
+            app_service_config_files = mkOption {
+              type = types.listOf types.path;
+              default = [ ];
+              description = ''
+                A list of application service config file to use
+              '';
+            };
+
+          };
+        };
+      };
+
+      extraConfigFiles = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          Extra config files to include.
+
+          The configuration files will be included based on the command line
+          argument --config-path. This allows to configure secrets without
+          having to go through the Nix store, e.g. based on deployment keys if
+          NixOps is in use.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      { assertion = hasLocalPostgresDB -> config.services.postgresql.enable;
+        message = ''
+          Cannot deploy matrix-synapse with a configuration for a local postgresql database
+            and a missing postgresql service. Since 20.03 it's mandatory to manually configure the
+            database (please read the thread in https://github.com/NixOS/nixpkgs/pull/80447 for
+            further reference).
+
+            If you
+            - try to deploy a fresh synapse, you need to configure the database yourself. An example
+              for this can be found in <nixpkgs/nixos/tests/matrix-synapse.nix>
+            - update your existing matrix-synapse instance, you simply need to add `services.postgresql.enable = true`
+              to your configuration.
+
+          For further information about this update, please read the release-notes of 20.03 carefully.
+        '';
+      }
+    ];
+
+    services.matrix-synapse.configFile = configFile;
+
+    users.users.matrix-synapse = {
+      group = "matrix-synapse";
+      home = cfg.dataDir;
+      createHome = true;
+      shell = "${pkgs.bash}/bin/bash";
+      uid = config.ids.uids.matrix-synapse;
+    };
+
+    users.groups.matrix-synapse = {
+      gid = config.ids.gids.matrix-synapse;
+    };
+
+    systemd.services.matrix-synapse = {
+      description = "Synapse Matrix homeserver";
+      after = [ "network.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        ${cfg.package}/bin/synapse_homeserver \
+          --config-path ${configFile} \
+          --keys-directory ${cfg.dataDir} \
+          --generate-keys
+      '';
+      environment = {
+        PYTHONPATH = makeSearchPathOutput "lib" cfg.package.python.sitePackages [ pluginsEnv ];
+      } // optionalAttrs (cfg.withJemalloc) {
+        LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so";
+      };
+      serviceConfig = {
+        Type = "notify";
+        User = "matrix-synapse";
+        Group = "matrix-synapse";
+        WorkingDirectory = cfg.dataDir;
+        ExecStartPre = [ ("+" + (pkgs.writeShellScript "matrix-synapse-fix-permissions" ''
+          chown matrix-synapse:matrix-synapse ${cfg.dataDir}/homeserver.signing.key
+          chmod 0600 ${cfg.dataDir}/homeserver.signing.key
+        '')) ];
+        ExecStart = ''
+          ${cfg.package}/bin/synapse_homeserver \
+            ${ concatMapStringsSep "\n  " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) }
+            --keys-directory ${cfg.dataDir}
+        '';
+        ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
+        Restart = "on-failure";
+        UMask = "0077";
+      };
+    };
+
+    environment.systemPackages = [ registerNewMatrixUser ];
+  };
+
+  meta = {
+    buildDocsInSandbox = false;
+    doc = ./matrix-synapse.xml;
+    maintainers = teams.matrix.members;
+  };
+
+}
diff --git a/nixos/modules/services/misc/matrix-synapse.xml b/nixos/modules/services/matrix/matrix-synapse.xml
index 41a56df0f2b..cdc4b4de1a7 100644
--- a/nixos/modules/services/misc/matrix-synapse.xml
+++ b/nixos/modules/services/matrix/matrix-synapse.xml
@@ -115,20 +115,21 @@ in {
   };
   services.matrix-synapse = {
     <link linkend="opt-services.matrix-synapse.enable">enable</link> = true;
-    <link linkend="opt-services.matrix-synapse.server_name">server_name</link> = config.networking.domain;
-    <link linkend="opt-services.matrix-synapse.listeners">listeners</link> = [
+    <link linkend="opt-services.matrix-synapse.settings.server_name">server_name</link> = config.networking.domain;
+    <link linkend="opt-services.matrix-synapse.settings.listeners">listeners</link> = [
       {
-        <link linkend="opt-services.matrix-synapse.listeners._.port">port</link> = 8008;
-        <link linkend="opt-services.matrix-synapse.listeners._.bind_address">bind_address</link> = "::1";
-        <link linkend="opt-services.matrix-synapse.listeners._.type">type</link> = "http";
-        <link linkend="opt-services.matrix-synapse.listeners._.tls">tls</link> = false;
-        <link linkend="opt-services.matrix-synapse.listeners._.x_forwarded">x_forwarded</link> = true;
-        <link linkend="opt-services.matrix-synapse.listeners._.resources">resources</link> = [
-          {
-            <link linkend="opt-services.matrix-synapse.listeners._.resources._.names">names</link> = [ "client" "federation" ];
-            <link linkend="opt-services.matrix-synapse.listeners._.resources._.compress">compress</link> = false;
-          }
-        ];
+        <link linkend="opt-services.matrix-synapse.settings.listeners._.port">port</link> = 8008;
+        <link linkend="opt-services.matrix-synapse.settings.listeners._.bind_addresses">bind_address</link> = [ "::1" ];
+        <link linkend="opt-services.matrix-synapse.settings.listeners._.type">type</link> = "http";
+        <link linkend="opt-services.matrix-synapse.settings.listeners._.tls">tls</link> = false;
+        <link linkend="opt-services.matrix-synapse.settings.listeners._.x_forwarded">x_forwarded</link> = true;
+        <link linkend="opt-services.matrix-synapse.settings.listeners._.resources">resources</link> = [ {
+          <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.names">names</link> = [ "client" ];
+          <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.compress">compress</link> = true;
+        } {
+          <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.names">names</link> = [ "federation" ];
+          <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.compress">compress</link> = false;
+        } ];
       }
     ];
   };
@@ -151,11 +152,11 @@ in {
 
   <para>
    If you want to run a server with public registration by anybody, you can
-   then enable <literal><link linkend="opt-services.matrix-synapse.enable_registration">services.matrix-synapse.enable_registration</link> =
+   then enable <literal><link linkend="opt-services.matrix-synapse.settings.enable_registration">services.matrix-synapse.enable_registration</link> =
    true;</literal>. Otherwise, or you can generate a registration secret with
    <command>pwgen -s 64 1</command> and set it with
-   <option><link linkend="opt-services.matrix-synapse.registration_shared_secret">services.matrix-synapse.registration_shared_secret</link></option>. To
-   create a new user or admin, run the following after you have set the secret
+   <option><link linkend="opt-services.matrix-synapse.settings.registration_shared_secret">services.matrix-synapse.registration_shared_secret</link></option>.
+   To create a new user or admin, run the following after you have set the secret
    and have rebuilt NixOS:
 <screen>
 <prompt>$ </prompt>nix run nixpkgs.matrix-synapse
@@ -170,7 +171,7 @@ Success!
    <literal>@your-username:example.org</literal>. Note that the registration
    secret ends up in the nix store and therefore is world-readable by any user
    on your machine, so it makes sense to only temporarily activate the
-   <link linkend="opt-services.matrix-synapse.registration_shared_secret">registration_shared_secret</link>
+   <link linkend="opt-services.matrix-synapse.settings.registration_shared_secret">registration_shared_secret</link>
    option until a better solution for NixOS is in place.
   </para>
  </section>
diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix
deleted file mode 100644
index feca4c5465f..00000000000
--- a/nixos/modules/services/misc/matrix-synapse.nix
+++ /dev/null
@@ -1,844 +0,0 @@
-{ config, lib, options, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.matrix-synapse;
-  opt = options.services.matrix-synapse;
-  pg = config.services.postgresql;
-  usePostgresql = cfg.database_type == "psycopg2";
-  logConfigFile = pkgs.writeText "log_config.yaml" cfg.logConfig;
-  mkResource = r: ''{names: ${builtins.toJSON r.names}, compress: ${boolToString r.compress}}'';
-  mkListener = l: ''{port: ${toString l.port}, bind_address: "${l.bind_address}", type: ${l.type}, tls: ${boolToString l.tls}, x_forwarded: ${boolToString l.x_forwarded}, resources: [${concatStringsSep "," (map mkResource l.resources)}]}'';
-  pluginsEnv = cfg.package.python.buildEnv.override {
-    extraLibs = cfg.plugins;
-  };
-  configFile = pkgs.writeText "homeserver.yaml" ''
-${optionalString (cfg.tls_certificate_path != null) ''
-tls_certificate_path: "${cfg.tls_certificate_path}"
-''}
-${optionalString (cfg.tls_private_key_path != null) ''
-tls_private_key_path: "${cfg.tls_private_key_path}"
-''}
-${optionalString (cfg.tls_dh_params_path != null) ''
-tls_dh_params_path: "${cfg.tls_dh_params_path}"
-''}
-no_tls: ${boolToString cfg.no_tls}
-${optionalString (cfg.bind_port != null) ''
-bind_port: ${toString cfg.bind_port}
-''}
-${optionalString (cfg.unsecure_port != null) ''
-unsecure_port: ${toString cfg.unsecure_port}
-''}
-${optionalString (cfg.bind_host != null) ''
-bind_host: "${cfg.bind_host}"
-''}
-server_name: "${cfg.server_name}"
-pid_file: "/run/matrix-synapse.pid"
-${optionalString (cfg.public_baseurl != null) ''
-public_baseurl: "${cfg.public_baseurl}"
-''}
-listeners: [${concatStringsSep "," (map mkListener cfg.listeners)}]
-database: {
-  name: "${cfg.database_type}",
-  args: {
-    ${concatStringsSep ",\n    " (
-      mapAttrsToList (n: v: "\"${n}\": ${builtins.toJSON v}") cfg.database_args
-    )}
-  }
-}
-event_cache_size: "${cfg.event_cache_size}"
-verbose: ${cfg.verbose}
-log_config: "${logConfigFile}"
-rc_messages_per_second: ${cfg.rc_messages_per_second}
-rc_message_burst_count: ${cfg.rc_message_burst_count}
-federation_rc_window_size: ${cfg.federation_rc_window_size}
-federation_rc_sleep_limit: ${cfg.federation_rc_sleep_limit}
-federation_rc_sleep_delay: ${cfg.federation_rc_sleep_delay}
-federation_rc_reject_limit: ${cfg.federation_rc_reject_limit}
-federation_rc_concurrent: ${cfg.federation_rc_concurrent}
-media_store_path: "${cfg.dataDir}/media"
-uploads_path: "${cfg.dataDir}/uploads"
-max_upload_size: "${cfg.max_upload_size}"
-max_image_pixels: "${cfg.max_image_pixels}"
-dynamic_thumbnails: ${boolToString cfg.dynamic_thumbnails}
-url_preview_enabled: ${boolToString cfg.url_preview_enabled}
-${optionalString (cfg.url_preview_enabled == true) ''
-url_preview_ip_range_blacklist: ${builtins.toJSON cfg.url_preview_ip_range_blacklist}
-url_preview_ip_range_whitelist: ${builtins.toJSON cfg.url_preview_ip_range_whitelist}
-url_preview_url_blacklist: ${builtins.toJSON cfg.url_preview_url_blacklist}
-''}
-recaptcha_private_key: "${cfg.recaptcha_private_key}"
-recaptcha_public_key: "${cfg.recaptcha_public_key}"
-enable_registration_captcha: ${boolToString cfg.enable_registration_captcha}
-turn_uris: ${builtins.toJSON cfg.turn_uris}
-turn_shared_secret: "${cfg.turn_shared_secret}"
-enable_registration: ${boolToString cfg.enable_registration}
-${optionalString (cfg.registration_shared_secret != null) ''
-registration_shared_secret: "${cfg.registration_shared_secret}"
-''}
-recaptcha_siteverify_api: "https://www.google.com/recaptcha/api/siteverify"
-turn_user_lifetime: "${cfg.turn_user_lifetime}"
-user_creation_max_duration: ${cfg.user_creation_max_duration}
-bcrypt_rounds: ${cfg.bcrypt_rounds}
-allow_guest_access: ${boolToString cfg.allow_guest_access}
-
-account_threepid_delegates:
-  ${optionalString (cfg.account_threepid_delegates.email != null) "email: ${cfg.account_threepid_delegates.email}"}
-  ${optionalString (cfg.account_threepid_delegates.msisdn != null) "msisdn: ${cfg.account_threepid_delegates.msisdn}"}
-
-room_prejoin_state:
-  disable_default_event_types: ${boolToString cfg.room_prejoin_state.disable_default_event_types}
-  additional_event_types: ${builtins.toJSON cfg.room_prejoin_state.additional_event_types}
-${optionalString (cfg.macaroon_secret_key != null) ''
-  macaroon_secret_key: "${cfg.macaroon_secret_key}"
-''}
-expire_access_token: ${boolToString cfg.expire_access_token}
-enable_metrics: ${boolToString cfg.enable_metrics}
-report_stats: ${boolToString cfg.report_stats}
-signing_key_path: "${cfg.dataDir}/homeserver.signing.key"
-key_refresh_interval: "${cfg.key_refresh_interval}"
-perspectives:
-  servers: {
-    ${concatStringsSep "},\n" (mapAttrsToList (n: v: ''
-    "${n}": {
-      "verify_keys": {
-        ${concatStringsSep "},\n" (mapAttrsToList (n: v: ''
-        "${n}": {
-          "key": "${v}"
-        }'') v)}
-      }
-    '') cfg.servers)}
-    }
-  }
-redaction_retention_period: ${toString cfg.redaction_retention_period}
-app_service_config_files: ${builtins.toJSON cfg.app_service_config_files}
-
-${cfg.extraConfig}
-'';
-
-  hasLocalPostgresDB = let args = cfg.database_args; in
-    usePostgresql && (!(args ? host) || (elem args.host [ "localhost" "127.0.0.1" "::1" ]));
-
-  registerNewMatrixUser =
-    let
-      isIpv6 = x: lib.length (lib.splitString ":" x) > 1;
-      listener =
-        lib.findFirst (
-          listener: lib.any (
-            resource: lib.any (
-              name: name == "client"
-            ) resource.names
-          ) listener.resources
-        ) (lib.last cfg.listeners) cfg.listeners;
-    in
-    pkgs.writeShellScriptBin "matrix-synapse-register_new_matrix_user" ''
-      exec ${cfg.package}/bin/register_new_matrix_user \
-        $@ \
-        ${lib.concatMapStringsSep " " (x: "-c ${x}") ([ configFile ] ++ cfg.extraConfigFiles)} \
-        "${listener.type}://${
-          if (isIpv6 listener.bind_address) then
-            "[${listener.bind_address}]"
-          else
-            "${listener.bind_address}"
-        }:${builtins.toString listener.port}/"
-    '';
-in {
-  options = {
-    services.matrix-synapse = {
-      enable = mkEnableOption "matrix.org synapse";
-      configFile = mkOption {
-        type = types.str;
-        readOnly = true;
-        description = ''
-          Path to the configuration file on the target system. Useful to configure e.g. workers
-          that also need this.
-        '';
-      };
-      package = mkOption {
-        type = types.package;
-        default = pkgs.matrix-synapse;
-        defaultText = literalExpression "pkgs.matrix-synapse";
-        description = ''
-          Overridable attribute of the matrix synapse server package to use.
-        '';
-      };
-      plugins = mkOption {
-        type = types.listOf types.package;
-        default = [ ];
-        example = literalExpression ''
-          with config.services.matrix-synapse.package.plugins; [
-            matrix-synapse-ldap3
-            matrix-synapse-pam
-          ];
-        '';
-        description = ''
-          List of additional Matrix plugins to make available.
-        '';
-      };
-      withJemalloc = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Whether to preload jemalloc to reduce memory fragmentation and overall usage.
-        '';
-      };
-      no_tls = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Don't bind to the https port
-        '';
-      };
-      bind_port = mkOption {
-        type = types.nullOr types.int;
-        default = null;
-        example = 8448;
-        description = ''
-          DEPRECATED: Use listeners instead.
-          The port to listen for HTTPS requests on.
-          For when matrix traffic is sent directly to synapse.
-        '';
-      };
-      unsecure_port = mkOption {
-        type = types.nullOr types.int;
-        default = null;
-        example = 8008;
-        description = ''
-          DEPRECATED: Use listeners instead.
-          The port to listen for HTTP requests on.
-          For when matrix traffic passes through loadbalancer that unwraps TLS.
-        '';
-      };
-      bind_host = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          DEPRECATED: Use listeners instead.
-          Local interface to listen on.
-          The empty string will cause synapse to listen on all interfaces.
-        '';
-      };
-      tls_certificate_path = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "/var/lib/matrix-synapse/homeserver.tls.crt";
-        description = ''
-          PEM encoded X509 certificate for TLS.
-          You can replace the self-signed certificate that synapse
-          autogenerates on launch with your own SSL certificate + key pair
-          if you like.  Any required intermediary certificates can be
-          appended after the primary certificate in hierarchical order.
-        '';
-      };
-      tls_private_key_path = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "/var/lib/matrix-synapse/homeserver.tls.key";
-        description = ''
-          PEM encoded private key for TLS. Specify null if synapse is not
-          speaking TLS directly.
-        '';
-      };
-      tls_dh_params_path = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "/var/lib/matrix-synapse/homeserver.tls.dh";
-        description = ''
-          PEM dh parameters for ephemeral keys
-        '';
-      };
-      server_name = mkOption {
-        type = types.str;
-        example = "example.com";
-        default = config.networking.hostName;
-        defaultText = literalExpression "config.networking.hostName";
-        description = ''
-          The domain name of the server, with optional explicit port.
-          This is used by remote servers to look up the server address.
-          This is also the last part of your UserID.
-
-          The server_name cannot be changed later so it is important to configure this correctly before you start Synapse.
-        '';
-      };
-      public_baseurl = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "https://example.com:8448/";
-        description = ''
-          The public-facing base URL for the client API (not including _matrix/...)
-        '';
-      };
-      listeners = mkOption {
-        type = types.listOf (types.submodule {
-          options = {
-            port = mkOption {
-              type = types.port;
-              example = 8448;
-              description = ''
-                The port to listen for HTTP(S) requests on.
-              '';
-            };
-            bind_address = mkOption {
-              type = types.str;
-              default = "";
-              example = "203.0.113.42";
-              description = ''
-                Local interface to listen on.
-                The empty string will cause synapse to listen on all interfaces.
-              '';
-            };
-            type = mkOption {
-              type = types.str;
-              default = "http";
-              description = ''
-                Type of listener.
-              '';
-            };
-            tls = mkOption {
-              type = types.bool;
-              default = true;
-              description = ''
-                Whether to listen for HTTPS connections rather than HTTP.
-              '';
-            };
-            x_forwarded = mkOption {
-              type = types.bool;
-              default = false;
-              description = ''
-                Use the X-Forwarded-For (XFF) header as the client IP and not the
-                actual client IP.
-              '';
-            };
-            resources = mkOption {
-              type = types.listOf (types.submodule {
-                options = {
-                  names = mkOption {
-                    type = types.listOf types.str;
-                    description = ''
-                      List of resources to host on this listener.
-                    '';
-                    example = ["client" "federation"];
-                  };
-                  compress = mkOption {
-                    type = types.bool;
-                    description = ''
-                      Should synapse compress HTTP responses to clients that support it?
-                      This should be disabled if running synapse behind a load balancer
-                      that can do automatic compression.
-                    '';
-                  };
-                };
-              });
-              description = ''
-                List of HTTP resources to serve on this listener.
-              '';
-            };
-          };
-        });
-        default = [{
-          port = 8448;
-          bind_address = "";
-          type = "http";
-          tls = true;
-          x_forwarded = false;
-          resources = [
-            { names = ["client"]; compress = true; }
-            { names = ["federation"]; compress = false; }
-          ];
-        }];
-        description = ''
-          List of ports that Synapse should listen on, their purpose and their configuration.
-        '';
-      };
-      verbose = mkOption {
-        type = types.str;
-        default = "0";
-        description = "Logging verbosity level.";
-      };
-      rc_messages_per_second = mkOption {
-        type = types.str;
-        default = "0.2";
-        description = "Number of messages a client can send per second";
-      };
-      rc_message_burst_count = mkOption {
-        type = types.str;
-        default = "10.0";
-        description = "Number of message a client can send before being throttled";
-      };
-      federation_rc_window_size = mkOption {
-        type = types.str;
-        default = "1000";
-        description = "The federation window size in milliseconds";
-      };
-      federation_rc_sleep_limit = mkOption {
-        type = types.str;
-        default = "10";
-        description = ''
-          The number of federation requests from a single server in a window
-          before the server will delay processing the request.
-        '';
-      };
-      federation_rc_sleep_delay = mkOption {
-        type = types.str;
-        default = "500";
-        description = ''
-          The duration in milliseconds to delay processing events from
-          remote servers by if they go over the sleep limit.
-        '';
-      };
-      federation_rc_reject_limit = mkOption {
-        type = types.str;
-        default = "50";
-        description = ''
-          The maximum number of concurrent federation requests allowed
-          from a single server
-        '';
-      };
-      federation_rc_concurrent = mkOption {
-        type = types.str;
-        default = "3";
-        description = "The number of federation requests to concurrently process from a single server";
-      };
-      database_type = mkOption {
-        type = types.enum [ "sqlite3" "psycopg2" ];
-        default = if versionAtLeast config.system.stateVersion "18.03"
-          then "psycopg2"
-          else "sqlite3";
-        defaultText = literalExpression ''
-          if versionAtLeast config.system.stateVersion "18.03"
-            then "psycopg2"
-            else "sqlite3"
-        '';
-        description = ''
-          The database engine name. Can be sqlite or psycopg2.
-        '';
-      };
-      database_name = mkOption {
-        type = types.str;
-        default = "matrix-synapse";
-        description = "Database name.";
-      };
-      database_user = mkOption {
-        type = types.str;
-        default = "matrix-synapse";
-        description = "Database user name.";
-      };
-      database_args = mkOption {
-        type = types.attrs;
-        default = {
-          sqlite3 = { database = "${cfg.dataDir}/homeserver.db"; };
-          psycopg2 = {
-            user = cfg.database_user;
-            database = cfg.database_name;
-          };
-        }.${cfg.database_type};
-        defaultText = literalDocBook ''
-          <variablelist>
-            <varlistentry>
-              <term>using sqlite3</term>
-              <listitem>
-                <programlisting>
-                  { database = "''${config.${opt.dataDir}}/homeserver.db"; }
-                </programlisting>
-              </listitem>
-            </varlistentry>
-            <varlistentry>
-              <term>using psycopg2</term>
-              <listitem>
-                <programlisting>
-                  psycopg2 = {
-                    user = config.${opt.database_user};
-                    database = config.${opt.database_name};
-                  }
-                </programlisting>
-              </listitem>
-            </varlistentry>
-          </variablelist>
-        '';
-        description = ''
-          Arguments to pass to the engine.
-        '';
-      };
-      event_cache_size = mkOption {
-        type = types.str;
-        default = "10K";
-        description = "Number of events to cache in memory.";
-      };
-      url_preview_enabled = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Is the preview URL API enabled?  If enabled, you *must* specify an
-          explicit url_preview_ip_range_blacklist of IPs that the spider is
-          denied from accessing.
-        '';
-      };
-      url_preview_ip_range_blacklist = mkOption {
-        type = types.listOf types.str;
-        default = [
-          "127.0.0.0/8"
-          "10.0.0.0/8"
-          "172.16.0.0/12"
-          "192.168.0.0/16"
-          "100.64.0.0/10"
-          "169.254.0.0/16"
-          "::1/128"
-          "fe80::/64"
-          "fc00::/7"
-        ];
-        description = ''
-          List of IP address CIDR ranges that the URL preview spider is denied
-          from accessing.
-        '';
-      };
-      url_preview_ip_range_whitelist = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          List of IP address CIDR ranges that the URL preview spider is allowed
-          to access even if they are specified in
-          url_preview_ip_range_blacklist.
-        '';
-      };
-      url_preview_url_blacklist = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Optional list of URL matches that the URL preview spider is
-          denied from accessing.
-        '';
-      };
-      recaptcha_private_key = mkOption {
-        type = types.str;
-        default = "";
-        description = ''
-          This Home Server's ReCAPTCHA private key.
-        '';
-      };
-      recaptcha_public_key = mkOption {
-        type = types.str;
-        default = "";
-        description = ''
-          This Home Server's ReCAPTCHA public key.
-        '';
-      };
-      enable_registration_captcha = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Enables ReCaptcha checks when registering, preventing signup
-          unless a captcha is answered. Requires a valid ReCaptcha
-          public/private key.
-        '';
-      };
-      turn_uris = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          The public URIs of the TURN server to give to clients
-        '';
-      };
-      turn_shared_secret = mkOption {
-        type = types.str;
-        default = "";
-        description = ''
-          The shared secret used to compute passwords for the TURN server
-        '';
-      };
-      turn_user_lifetime = mkOption {
-        type = types.str;
-        default = "1h";
-        description = "How long generated TURN credentials last";
-      };
-      enable_registration = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Enable registration for new users.
-        '';
-      };
-      registration_shared_secret = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          If set, allows registration by anyone who also has the shared
-          secret, even if registration is otherwise disabled.
-        '';
-      };
-      enable_metrics = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Enable collection and rendering of performance metrics
-        '';
-      };
-      report_stats = mkOption {
-        type = types.bool;
-        default = false;
-        description = "";
-      };
-      servers = mkOption {
-        type = types.attrsOf (types.attrsOf types.str);
-        default = {
-          "matrix.org" = {
-            "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
-          };
-        };
-        description = ''
-          The trusted servers to download signing keys from.
-        '';
-      };
-      max_upload_size = mkOption {
-        type = types.str;
-        default = "10M";
-        description = "The largest allowed upload size in bytes";
-      };
-      max_image_pixels = mkOption {
-        type = types.str;
-        default = "32M";
-        description = "Maximum number of pixels that will be thumbnailed";
-      };
-      dynamic_thumbnails = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Whether to generate new thumbnails on the fly to precisely match
-          the resolution requested by the client. If true then whenever
-          a new resolution is requested by the client the server will
-          generate a new thumbnail. If false the server will pick a thumbnail
-          from a precalculated list.
-        '';
-      };
-      user_creation_max_duration = mkOption {
-        type = types.str;
-        default = "1209600000";
-        description = ''
-          Sets the expiry for the short term user creation in
-          milliseconds. The default value is two weeks.
-        '';
-      };
-      bcrypt_rounds = mkOption {
-        type = types.str;
-        default = "12";
-        description = ''
-          Set the number of bcrypt rounds used to generate password hash.
-          Larger numbers increase the work factor needed to generate the hash.
-        '';
-      };
-      allow_guest_access = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Allows users to register as guests without a password/email/etc, and
-          participate in rooms hosted on this server which have been made
-          accessible to anonymous users.
-        '';
-      };
-      account_threepid_delegates.email = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Delegate email sending to https://example.org
-        '';
-      };
-      account_threepid_delegates.msisdn = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Delegate SMS sending to this local process (https://localhost:8090)
-        '';
-      };
-      room_prejoin_state.additional_event_types = mkOption {
-        default = [];
-        type = types.listOf types.str;
-        description = ''
-          Additional events to share with users who received an invite.
-        '';
-      };
-      room_prejoin_state.disable_default_event_types = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Whether to disable the default state-event types for users invited to a room.
-          These are:
-
-          <itemizedlist>
-          <listitem><para>m.room.join_rules</para></listitem>
-          <listitem><para>m.room.canonical_alias</para></listitem>
-          <listitem><para>m.room.avatar</para></listitem>
-          <listitem><para>m.room.encryption</para></listitem>
-          <listitem><para>m.room.name</para></listitem>
-          <listitem><para>m.room.create</para></listitem>
-          </itemizedlist>
-        '';
-      };
-      macaroon_secret_key = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Secret key for authentication tokens
-        '';
-      };
-      expire_access_token = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Whether to enable access token expiration.
-        '';
-      };
-      key_refresh_interval = mkOption {
-        type = types.str;
-        default = "1d";
-        description = ''
-          How long key response published by this server is valid for.
-          Used to set the valid_until_ts in /key/v2 APIs.
-          Determines how quickly servers will query to check which keys
-          are still valid.
-        '';
-      };
-      app_service_config_files = mkOption {
-        type = types.listOf types.path;
-        default = [ ];
-        description = ''
-          A list of application service config file to use
-        '';
-      };
-      redaction_retention_period = mkOption {
-        type = types.int;
-        default = 7;
-        description = ''
-          How long to keep redacted events in unredacted form in the database.
-        '';
-      };
-      extraConfig = mkOption {
-        type = types.lines;
-        default = "";
-        description = ''
-          Extra config options for matrix-synapse.
-        '';
-      };
-      extraConfigFiles = mkOption {
-        type = types.listOf types.path;
-        default = [];
-        description = ''
-          Extra config files to include.
-
-          The configuration files will be included based on the command line
-          argument --config-path. This allows to configure secrets without
-          having to go through the Nix store, e.g. based on deployment keys if
-          NixOPS is in use.
-        '';
-      };
-      logConfig = mkOption {
-        type = types.lines;
-        default = readFile ./matrix-synapse-log_config.yaml;
-        description = ''
-          A yaml python logging config file
-        '';
-      };
-      dataDir = mkOption {
-        type = types.str;
-        default = "/var/lib/matrix-synapse";
-        description = ''
-          The directory where matrix-synapse stores its stateful data such as
-          certificates, media and uploads.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    assertions = [
-      { assertion = hasLocalPostgresDB -> config.services.postgresql.enable;
-        message = ''
-          Cannot deploy matrix-synapse with a configuration for a local postgresql database
-            and a missing postgresql service. Since 20.03 it's mandatory to manually configure the
-            database (please read the thread in https://github.com/NixOS/nixpkgs/pull/80447 for
-            further reference).
-
-            If you
-            - try to deploy a fresh synapse, you need to configure the database yourself. An example
-              for this can be found in <nixpkgs/nixos/tests/matrix-synapse.nix>
-            - update your existing matrix-synapse instance, you simply need to add `services.postgresql.enable = true`
-              to your configuration.
-
-          For further information about this update, please read the release-notes of 20.03 carefully.
-        '';
-      }
-    ];
-
-    services.matrix-synapse.configFile = "${configFile}";
-
-    users.users.matrix-synapse = {
-      group = "matrix-synapse";
-      home = cfg.dataDir;
-      createHome = true;
-      shell = "${pkgs.bash}/bin/bash";
-      uid = config.ids.uids.matrix-synapse;
-    };
-
-    users.groups.matrix-synapse = {
-      gid = config.ids.gids.matrix-synapse;
-    };
-
-    systemd.services.matrix-synapse = {
-      description = "Synapse Matrix homeserver";
-      after = [ "network.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
-      wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        ${cfg.package}/bin/synapse_homeserver \
-          --config-path ${configFile} \
-          --keys-directory ${cfg.dataDir} \
-          --generate-keys
-      '';
-      environment = {
-        PYTHONPATH = makeSearchPathOutput "lib" cfg.package.python.sitePackages [ pluginsEnv ];
-      } // optionalAttrs (cfg.withJemalloc) {
-        LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so";
-      };
-      serviceConfig = {
-        Type = "notify";
-        User = "matrix-synapse";
-        Group = "matrix-synapse";
-        WorkingDirectory = cfg.dataDir;
-        ExecStartPre = [ ("+" + (pkgs.writeShellScript "matrix-synapse-fix-permissions" ''
-          chown matrix-synapse:matrix-synapse ${cfg.dataDir}/homeserver.signing.key
-          chmod 0600 ${cfg.dataDir}/homeserver.signing.key
-        '')) ];
-        ExecStart = ''
-          ${cfg.package}/bin/synapse_homeserver \
-            ${ concatMapStringsSep "\n  " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) }
-            --keys-directory ${cfg.dataDir}
-        '';
-        ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
-        Restart = "on-failure";
-        UMask = "0077";
-      };
-    };
-
-    environment.systemPackages = [ registerNewMatrixUser ];
-  };
-
-  imports = [
-    (mkRemovedOptionModule [ "services" "matrix-synapse" "trusted_third_party_id_servers" ] ''
-      The `trusted_third_party_id_servers` option as been removed in `matrix-synapse` v1.4.0
-      as the behavior is now obsolete.
-    '')
-    (mkRemovedOptionModule [ "services" "matrix-synapse" "create_local_database" ] ''
-      Database configuration must be done manually. An exemplary setup is demonstrated in
-      <nixpkgs/nixos/tests/matrix-synapse.nix>
-    '')
-    (mkRemovedOptionModule [ "services" "matrix-synapse" "web_client" ] "")
-    (mkRemovedOptionModule [ "services" "matrix-synapse" "room_invite_state_types" ] ''
-      You may add additional event types via
-      `services.matrix-synapse.room_prejoin_state.additional_event_types` and
-      disable the default events via
-      `services.matrix-synapse.room_prejoin_state.disable_default_event_types`.
-    '')
-  ];
-
-  meta.doc = ./matrix-synapse.xml;
-  meta.maintainers = teams.matrix.members;
-
-}
diff --git a/nixos/tests/matrix-appservice-irc.nix b/nixos/tests/matrix-appservice-irc.nix
index e1da410af06..d1c561f95db 100644
--- a/nixos/tests/matrix-appservice-irc.nix
+++ b/nixos/tests/matrix-appservice-irc.nix
@@ -1,6 +1,6 @@
 import ./make-test-python.nix ({ pkgs, ... }:
   let
-    homeserverUrl = "http://homeserver:8448";
+    homeserverUrl = "http://homeserver:8008";
   in
   {
     name = "matrix-appservice-irc";
@@ -14,28 +14,32 @@ import ./make-test-python.nix ({ pkgs, ... }:
         specialisation.running.configuration = {
           services.matrix-synapse = {
             enable = true;
-            database_type = "sqlite3";
-            app_service_config_files = [ "/registration.yml" ];
-
-            enable_registration = true;
-
-            listeners = [
-              # The default but tls=false
-              {
-                "bind_address" = "";
-                "port" = 8448;
-                "resources" = [
-                  { "compress" = true; "names" = [ "client" ]; }
-                  { "compress" = false; "names" = [ "federation" ]; }
+            settings = {
+              database.name = "sqlite3";
+              app_service_config_files = [ "/registration.yml" ];
+
+              enable_registration = true;
+
+              listeners = [ {
+                # The default but tls=false
+                bind_addresses = [
+                  "0.0.0.0"
                 ];
-                "tls" = false;
-                "type" = "http";
-                "x_forwarded" = false;
-              }
-            ];
+                port = 8008;
+                resources = [ {
+                  "compress" = true;
+                  "names" = [ "client" ];
+                } {
+                  "compress" = false;
+                  "names" = [ "federation" ];
+                } ];
+                tls = false;
+                type = "http";
+              } ];
+            };
           };
 
-          networking.firewall.allowedTCPPorts = [ 8448 ];
+          networking.firewall.allowedTCPPorts = [ 8008 ];
         };
       };
 
@@ -209,7 +213,7 @@ import ./make-test-python.nix ({ pkgs, ... }:
           )
 
           homeserver.wait_for_unit("matrix-synapse.service")
-          homeserver.wait_for_open_port(8448)
+          homeserver.wait_for_open_port(8008)
 
       with subtest("ensure messages can be exchanged"):
           client.succeed("do_test ${homeserverUrl} >&2")
diff --git a/nixos/tests/matrix-synapse.nix b/nixos/tests/matrix-synapse.nix
index 21e8c24e471..1ff1e47b284 100644
--- a/nixos/tests/matrix-synapse.nix
+++ b/nixos/tests/matrix-synapse.nix
@@ -33,6 +33,29 @@ import ./make-test-python.nix ({ pkgs, ... } : let
   testUser = "alice";
   testPassword = "alicealice";
   testEmail = "alice@example.com";
+
+  listeners = [ {
+    port = 8448;
+    bind_addresses = [
+      "127.0.0.1"
+      "::1"
+    ];
+    type = "http";
+    tls = true;
+    x_forwarded = false;
+    resources = [ {
+      names = [
+        "client"
+      ];
+      compress = true;
+    } {
+      names = [
+        "federation"
+      ];
+      compress = false;
+    } ];
+  } ];
+
 in {
 
   name = "matrix-synapse";
@@ -48,22 +71,24 @@ in {
     {
       services.matrix-synapse = {
         enable = true;
-        database_type = "psycopg2";
-        tls_certificate_path = "${cert}";
-        tls_private_key_path = "${key}";
-        database_args = {
-          password = "synapse";
+        settings = {
+          inherit listeners;
+          database = {
+            name = "psycopg2";
+            args.password = "synapse";
+          };
+          tls_certificate_path = "${cert}";
+          tls_private_key_path = "${key}";
+          registration_shared_secret = registrationSharedSecret;
+          public_baseurl = "https://example.com";
+          email = {
+            smtp_host = mailerDomain;
+            smtp_port = 25;
+            require_transport_security = true;
+            notif_from = "matrix <matrix@${mailerDomain}>";
+            app_name = "Matrix";
+          };
         };
-        registration_shared_secret = registrationSharedSecret;
-        public_baseurl = "https://example.com";
-        extraConfig = ''
-          email:
-            smtp_host: "${mailerDomain}"
-            smtp_port: 25
-            require_transport_security: true
-            notif_from: "matrix <matrix@${mailerDomain}>"
-            app_name: "Matrix"
-        '';
       };
       services.postgresql = {
         enable = true;
@@ -165,9 +190,12 @@ in {
     serversqlite = args: {
       services.matrix-synapse = {
         enable = true;
-        database_type = "sqlite3";
-        tls_certificate_path = "${cert}";
-        tls_private_key_path = "${key}";
+        settings = {
+          inherit listeners;
+          database.name = "sqlite3";
+          tls_certificate_path = "${cert}";
+          tls_private_key_path = "${key}";
+        };
       };
     };
   };
diff --git a/nixos/tests/matrix/mjolnir.nix b/nixos/tests/matrix/mjolnir.nix
index bb55f6f5440..54094ab9d61 100644
--- a/nixos/tests/matrix/mjolnir.nix
+++ b/nixos/tests/matrix/mjolnir.nix
@@ -38,26 +38,31 @@ import ../make-test-python.nix (
       homeserver = { pkgs, ... }: {
         services.matrix-synapse = {
           enable = true;
-          database_type = "sqlite3";
-          tls_certificate_path = "${cert}";
-          tls_private_key_path = "${key}";
-          enable_registration = true;
-          registration_shared_secret = "supersecret-registration";
-
-          listeners = [
-            # The default but tls=false
-            {
-              "bind_address" = "";
-              "port" = 8448;
-              "resources" = [
-                { "compress" = true; "names" = [ "client" "webclient" ]; }
-                { "compress" = false; "names" = [ "federation" ]; }
+          settings = {
+            database.name = "sqlite3";
+            tls_certificate_path = "${cert}";
+            tls_private_key_path = "${key}";
+            enable_registration = true;
+            registration_shared_secret = "supersecret-registration";
+
+            listeners = [ {
+              # The default but tls=false
+              bind_addresses = [
+                "0.0.0.0"
               ];
-              "tls" = false;
-              "type" = "http";
-              "x_forwarded" = false;
-            }
-          ];
+              port = 8448;
+              resources = [ {
+                compress = true;
+                names = [ "client" ];
+              } {
+                compress = false;
+                names = [ "federation" ];
+              } ];
+              tls = false;
+              type = "http";
+              x_forwarded = false;
+            } ];
+          };
         };
 
         networking.firewall.allowedTCPPorts = [ 8448 ];
diff --git a/nixos/tests/matrix/pantalaimon.nix b/nixos/tests/matrix/pantalaimon.nix
index fcb9904b213..1a9894dd215 100644
--- a/nixos/tests/matrix/pantalaimon.nix
+++ b/nixos/tests/matrix/pantalaimon.nix
@@ -47,9 +47,32 @@ import ../make-test-python.nix (
 
       services.matrix-synapse = {
         enable = true;
-        database_type = "sqlite3";
-        tls_certificate_path = "${cert}";
-        tls_private_key_path = "${key}";
+        settings = {
+          listeners = [ {
+            port = 8448;
+            bind_addresses = [
+              "127.0.0.1"
+              "::1"
+            ];
+            type = "http";
+            tls = true;
+            x_forwarded = false;
+            resources = [ {
+              names = [
+                "client"
+              ];
+              compress = true;
+            } {
+              names = [
+                "federation"
+              ];
+              compress = false;
+            } ];
+          } ];
+          database.name = "sqlite3";
+          tls_certificate_path = "${cert}";
+          tls_private_key_path = "${key}";
+        };
       };
     };