1 { config, lib, options, pkgs, ... }:
6 cfg = config.services.matrix-synapse;
7 format = pkgs.formats.yaml { };
9 filterRecursiveNull = o:
11 mapAttrs (_: v: filterRecursiveNull v) (filterAttrs (_: v: v != null) o)
13 map filterRecursiveNull (filter (v: v != null) o)
17 # remove null values from the final configuration
18 finalSettings = filterRecursiveNull cfg.settings;
19 configFile = format.generate "homeserver.yaml" finalSettings;
21 usePostgresql = cfg.settings.database.name == "psycopg2";
22 hasLocalPostgresDB = let args = cfg.settings.database.args; in
24 && (!(args ? host) || (elem args.host [ "localhost" "127.0.0.1" "::1" ]))
25 && config.services.postgresql.enable;
26 hasWorkers = cfg.workers != { };
28 listenerSupportsResource = resource: listener:
29 lib.any ({ names, ... }: builtins.elem resource names) listener.resources;
31 clientListener = findFirst
32 (listenerSupportsResource "client")
34 (cfg.settings.listeners
35 ++ concatMap ({ worker_listeners, ... }: worker_listeners) (attrValues cfg.workers));
37 registerNewMatrixUser =
39 isIpv6 = hasInfix ":";
41 # add a tail, so that without any bind_addresses we still have a useable address
42 bindAddress = head (clientListener.bind_addresses ++ [ "127.0.0.1" ]);
43 listenerProtocol = if clientListener.tls
47 assert assertMsg (clientListener != null) "No client listener found in synapse or one of its workers";
48 pkgs.writeShellScriptBin "matrix-synapse-register_new_matrix_user" ''
49 exec ${cfg.package}/bin/register_new_matrix_user \
51 ${lib.concatMapStringsSep " " (x: "-c ${x}") ([ configFile ] ++ cfg.extraConfigFiles)} \
52 "${listenerProtocol}://${
53 if (isIpv6 bindAddress) then
57 }:${builtins.toString clientListener.port}/"
67 wantedExtras = cfg.extras
68 ++ lib.optional (cfg.settings ? oidc_providers) "oidc"
69 ++ lib.optional (cfg.settings ? jwt_config) "jwt"
70 ++ lib.optional (cfg.settings ? saml2_config) "saml2"
71 ++ lib.optional (cfg.settings ? redis) "redis"
72 ++ lib.optional (cfg.settings ? sentry) "sentry"
73 ++ lib.optional (cfg.settings ? user_directory) "user-search"
74 ++ lib.optional (cfg.settings.url_preview_enabled) "url-preview"
75 ++ lib.optional (cfg.settings.database.name == "psycopg2") "postgres";
77 wrapped = pkgs.matrix-synapse.override {
78 extras = wantedExtras;
79 inherit (cfg) plugins;
82 defaultCommonLogConfig = {
84 formatters.journal_fmt.format = "%(name)s: [%(request)s] %(message)s";
86 class = "systemd.journal.JournalHandler";
87 formatter = "journal_fmt";
91 handlers = [ "journal" ];
93 disable_existing_loggers = false;
96 defaultCommonLogConfigText = generators.toPretty { } defaultCommonLogConfig;
98 logConfigText = logName:
100 Path to a yaml file generated from this Nix expression:
103 ${generators.toPretty { } (
104 recursiveUpdate defaultCommonLogConfig { handlers.journal.SYSLOG_IDENTIFIER = logName; }
109 genLogConfigFile = logName: format.generate
110 "synapse-log-${logName}.yaml"
111 (cfg.log // optionalAttrs (cfg.log?handlers.journal) {
112 handlers.journal = cfg.log.handlers.journal // {
113 SYSLOG_IDENTIFIER = logName;
119 lib.stringToCharacters
121 (lib.foldl (acc: digit: acc * 8 + digit) 0)
124 toDecimalFilePermission = value:
125 if value == null then
133 (mkRemovedOptionModule [ "services" "matrix-synapse" "trusted_third_party_id_servers" ] ''
134 The `trusted_third_party_id_servers` option as been removed in `matrix-synapse` v1.4.0
135 as the behavior is now obsolete.
137 (mkRemovedOptionModule [ "services" "matrix-synapse" "create_local_database" ] ''
138 Database configuration must be done manually. An exemplary setup is demonstrated in
139 <nixpkgs/nixos/tests/matrix/synapse.nix>
141 (mkRemovedOptionModule [ "services" "matrix-synapse" "web_client" ] "")
142 (mkRemovedOptionModule [ "services" "matrix-synapse" "room_invite_state_types" ] ''
143 You may add additional event types via
144 `services.matrix-synapse.room_prejoin_state.additional_event_types` and
145 disable the default events via
146 `services.matrix-synapse.room_prejoin_state.disable_default_event_types`.
149 # options that don't exist in synapse anymore
150 (mkRemovedOptionModule [ "services" "matrix-synapse" "bind_host" ] "Use listener settings instead." )
151 (mkRemovedOptionModule [ "services" "matrix-synapse" "bind_port" ] "Use listener settings instead." )
152 (mkRemovedOptionModule [ "services" "matrix-synapse" "expire_access_tokens" ] "" )
153 (mkRemovedOptionModule [ "services" "matrix-synapse" "no_tls" ] "It is no longer supported by synapse." )
154 (mkRemovedOptionModule [ "services" "matrix-synapse" "tls_dh_param_path" ] "It was removed from synapse." )
155 (mkRemovedOptionModule [ "services" "matrix-synapse" "unsecure_port" ] "Use settings.listeners instead." )
156 (mkRemovedOptionModule [ "services" "matrix-synapse" "user_creation_max_duration" ] "It is no longer supported by synapse." )
157 (mkRemovedOptionModule [ "services" "matrix-synapse" "verbose" ] "Use a log config instead." )
159 # options that were moved into rfc42 style settings
160 (mkRemovedOptionModule [ "services" "matrix-synapse" "app_service_config_files" ] "Use settings.app_service_config_files instead" )
161 (mkRemovedOptionModule [ "services" "matrix-synapse" "database_args" ] "Use settings.database.args instead" )
162 (mkRemovedOptionModule [ "services" "matrix-synapse" "database_name" ] "Use settings.database.args.database instead" )
163 (mkRemovedOptionModule [ "services" "matrix-synapse" "database_type" ] "Use settings.database.name instead" )
164 (mkRemovedOptionModule [ "services" "matrix-synapse" "database_user" ] "Use settings.database.args.user instead" )
165 (mkRemovedOptionModule [ "services" "matrix-synapse" "dynamic_thumbnails" ] "Use settings.dynamic_thumbnails instead" )
166 (mkRemovedOptionModule [ "services" "matrix-synapse" "enable_metrics" ] "Use settings.enable_metrics instead" )
167 (mkRemovedOptionModule [ "services" "matrix-synapse" "enable_registration" ] "Use settings.enable_registration instead" )
168 (mkRemovedOptionModule [ "services" "matrix-synapse" "extraConfig" ] "Use settings instead." )
169 (mkRemovedOptionModule [ "services" "matrix-synapse" "listeners" ] "Use settings.listeners instead" )
170 (mkRemovedOptionModule [ "services" "matrix-synapse" "logConfig" ] "Use settings.log_config instead" )
171 (mkRemovedOptionModule [ "services" "matrix-synapse" "max_image_pixels" ] "Use settings.max_image_pixels instead" )
172 (mkRemovedOptionModule [ "services" "matrix-synapse" "max_upload_size" ] "Use settings.max_upload_size instead" )
173 (mkRemovedOptionModule [ "services" "matrix-synapse" "presence" "enabled" ] "Use settings.presence.enabled instead" )
174 (mkRemovedOptionModule [ "services" "matrix-synapse" "public_baseurl" ] "Use settings.public_baseurl instead" )
175 (mkRemovedOptionModule [ "services" "matrix-synapse" "report_stats" ] "Use settings.report_stats instead" )
176 (mkRemovedOptionModule [ "services" "matrix-synapse" "server_name" ] "Use settings.server_name instead" )
177 (mkRemovedOptionModule [ "services" "matrix-synapse" "servers" ] "Use settings.trusted_key_servers instead." )
178 (mkRemovedOptionModule [ "services" "matrix-synapse" "tls_certificate_path" ] "Use settings.tls_certificate_path instead" )
179 (mkRemovedOptionModule [ "services" "matrix-synapse" "tls_private_key_path" ] "Use settings.tls_private_key_path instead" )
180 (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_shared_secret" ] "Use settings.turn_shared_secret instead" )
181 (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_uris" ] "Use settings.turn_uris instead" )
182 (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_user_lifetime" ] "Use settings.turn_user_lifetime instead" )
183 (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_enabled" ] "Use settings.url_preview_enabled instead" )
184 (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_ip_range_blacklist" ] "Use settings.url_preview_ip_range_blacklist instead" )
185 (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_ip_range_whitelist" ] "Use settings.url_preview_ip_range_whitelist instead" )
186 (mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_url_blacklist" ] "Use settings.url_preview_url_blacklist instead" )
188 # options that are too specific to mention them explicitly in settings
189 (mkRemovedOptionModule [ "services" "matrix-synapse" "account_threepid_delegates" "email" ] "Use settings.account_threepid_delegates.email instead" )
190 (mkRemovedOptionModule [ "services" "matrix-synapse" "account_threepid_delegates" "msisdn" ] "Use settings.account_threepid_delegates.msisdn instead" )
191 (mkRemovedOptionModule [ "services" "matrix-synapse" "allow_guest_access" ] "Use settings.allow_guest_access instead" )
192 (mkRemovedOptionModule [ "services" "matrix-synapse" "bcrypt_rounds" ] "Use settings.bcrypt_rounds instead" )
193 (mkRemovedOptionModule [ "services" "matrix-synapse" "enable_registration_captcha" ] "Use settings.enable_registration_captcha instead" )
194 (mkRemovedOptionModule [ "services" "matrix-synapse" "event_cache_size" ] "Use settings.event_cache_size instead" )
195 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_concurrent" ] "Use settings.rc_federation.concurrent instead" )
196 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_reject_limit" ] "Use settings.rc_federation.reject_limit instead" )
197 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_sleep_delay" ] "Use settings.rc_federation.sleep_delay instead" )
198 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_sleep_limit" ] "Use settings.rc_federation.sleep_limit instead" )
199 (mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_window_size" ] "Use settings.rc_federation.window_size instead" )
200 (mkRemovedOptionModule [ "services" "matrix-synapse" "key_refresh_interval" ] "Use settings.key_refresh_interval instead" )
201 (mkRemovedOptionModule [ "services" "matrix-synapse" "rc_messages_burst_count" ] "Use settings.rc_messages.burst_count instead" )
202 (mkRemovedOptionModule [ "services" "matrix-synapse" "rc_messages_per_second" ] "Use settings.rc_messages.per_second instead" )
203 (mkRemovedOptionModule [ "services" "matrix-synapse" "recaptcha_private_key" ] "Use settings.recaptcha_private_key instead" )
204 (mkRemovedOptionModule [ "services" "matrix-synapse" "recaptcha_public_key" ] "Use settings.recaptcha_public_key instead" )
205 (mkRemovedOptionModule [ "services" "matrix-synapse" "redaction_retention_period" ] "Use settings.redaction_retention_period instead" )
206 (mkRemovedOptionModule [ "services" "matrix-synapse" "room_prejoin_state" "additional_event_types" ] "Use settings.room_prejoin_state.additional_event_types instead" )
207 (mkRemovedOptionModule [ "services" "matrix-synapse" "room_prejoin_state" "disable_default_event_types" ] "Use settings.room_prejoin-state.disable_default_event_types instead" )
209 # Options that should be passed via extraConfigFiles, so they are not persisted into the nix store
210 (mkRemovedOptionModule [ "services" "matrix-synapse" "macaroon_secret_key" ] "Pass this value via extraConfigFiles instead" )
211 (mkRemovedOptionModule [ "services" "matrix-synapse" "registration_shared_secret" ] "Pass this value via extraConfigFiles instead" )
216 listenerType = workerContext: types.submodule ({ config, ... }: {
219 type = types.nullOr types.port;
223 The port to listen for HTTP(S) requests on.
227 bind_addresses = mkOption {
228 type = types.nullOr (types.listOf types.str);
229 default = if config.path != null then null else [
233 defaultText = literalExpression ''
242 example = literalExpression ''
249 IP addresses to bind the listener to.
254 type = types.nullOr types.path;
257 Unix domain socket path to bind this listener to.
260 This option is incompatible with {option}`bind_addresses`, {option}`port`, {option}`tls`
261 and also does not support the `metrics` and `manhole` listener {option}`type`.
267 type = types.nullOr (types.strMatching "^[0,2-7]{3,4}$");
268 default = if config.path != null then "660" else null;
269 defaultText = literalExpression ''
277 File permissions on the UNIX domain socket.
279 apply = toDecimalFilePermission;
292 The type of the listener, usually http.
297 type = types.nullOr types.bool;
298 default = if config.path != null then
303 Enabled for the main instance listener, unless it is configured with a UNIX domain socket path.
307 Whether to enable TLS on the listener socket.
310 This option will be ignored for UNIX domain sockets.
315 x_forwarded = mkOption {
317 default = config.path != null;
319 Enabled if the listener is configured with a UNIX domain socket path
323 Use the X-Forwarded-For (XFF) header as the client IP and not the
328 resources = mkOption {
329 type = types.listOf (types.submodule {
332 type = types.listOf (types.enum [
345 List of resources to host on this listener.
351 compress = mkOption {
355 Whether synapse should compress HTTP responses to clients that support it.
356 This should be disabled if running synapse behind a load balancer
357 that can do automatic compression.
363 List of HTTP resources to serve on this listener.
369 services.matrix-synapse = {
370 enable = mkEnableOption "matrix.org synapse, the reference homeserver";
372 enableRegistrationScript = mkOption {
374 default = clientListener.bind_addresses != [];
377 Enabled if the client listener uses TCP sockets
380 Whether to install the `register_new_matrix_user` script, that
381 allows account creation on the terminal.
384 This script does not work when the client listener uses UNIX domain sockets
389 serviceUnit = lib.mkOption {
390 type = lib.types.str;
393 The systemd unit (a service or a target) for other services to depend on if they
394 need to be started after matrix-synapse.
396 This option is useful as the actual parent unit for all matrix-synapse processes
397 changes when configuring workers.
401 configFile = mkOption {
405 Path to the configuration file on the target system. Useful to configure e.g. workers
411 type = types.package;
414 Reference to the `matrix-synapse` wrapper with all extras
415 (e.g. for `oidc` or `saml2`) added to the `PYTHONPATH` of all executables.
417 This option is useful to reference the "final" `matrix-synapse` package that's
418 actually used by `matrix-synapse.service`. For instance, when using
419 workers, it's possible to run
420 `''${config.services.matrix-synapse.package}/bin/synapse_worker` and
421 no additional PYTHONPATH needs to be specified for extras or plugins configured
422 via `services.matrix-synapse`.
424 However, this means that this option is supposed to be only declared
425 by the `services.matrix-synapse` module itself and is thus read-only.
426 In order to modify `matrix-synapse` itself, use an overlay to override
427 `pkgs.matrix-synapse-unwrapped`.
432 type = types.listOf (types.enum (lib.attrNames pkgs.matrix-synapse-unwrapped.optional-dependencies));
433 default = defaultExtras;
434 example = literalExpression ''
436 "cache-memory" # Provide statistics about caching memory consumption
437 "jwt" # JSON Web Token authentication
438 "oidc" # OpenID Connect authentication
439 "postgres" # PostgreSQL database backend
440 "redis" # Redis support for the replication stream between worker processes
441 "saml2" # SAML2 authentication
442 "sentry" # Error tracking and performance metrics
443 "systemd" # Provide the JournalHandler used in the default log_config
444 "url-preview" # Support for oEmbed URL previews
445 "user-search" # Support internationalized domain names in user-search
449 Explicitly install extras provided by matrix-synapse. Most
450 will require some additional configuration.
452 Extras will automatically be enabled, when the relevant
453 configuration sections are present.
455 Please note that this option is additive: i.e. when adding a new item
456 to this list, the defaults are still kept. To override the defaults as well,
462 type = types.listOf types.package;
464 example = literalExpression ''
465 with config.services.matrix-synapse.package.plugins; [
471 List of additional Matrix plugins to make available.
475 withJemalloc = mkOption {
479 Whether to preload jemalloc to reduce memory fragmentation and overall usage.
485 default = "/var/lib/matrix-synapse";
487 The directory where matrix-synapse stores its stateful data such as
488 certificates, media and uploads.
493 type = types.attrsOf format.type;
494 defaultText = literalExpression defaultCommonLogConfigText;
496 Default configuration for the loggers used by `matrix-synapse` and its workers.
497 The defaults are added with the default priority which means that
498 these will be merged with additional declarations. These additional
499 declarations also take precedence over the defaults when declared
500 with at least normal priority. For instance
501 the log-level for synapse and its workers can be changed like this:
505 services.matrix-synapse.log.root.level = "WARNING";
509 And another field can be added like this:
513 services.matrix-synapse.log = {
514 loggers."synapse.http.matrixfederationclient".level = "DEBUG";
519 Additionally, the field `handlers.journal.SYSLOG_IDENTIFIER` will be added to
520 each log config, i.e.
521 * `synapse` for `matrix-synapse.service`
522 * `synapse-<worker name>` for `matrix-synapse-worker-<worker name>.service`
524 This is only done if this option has a `handlers.journal` field declared.
526 To discard all settings declared by this option for each worker and synapse,
527 `lib.mkForce` can be used.
529 To discard all settings declared by this option for a single worker or synapse only,
530 [](#opt-services.matrix-synapse.workers._name_.worker_log_config) or
531 [](#opt-services.matrix-synapse.settings.log_config) can be used.
535 settings = mkOption {
538 The primary synapse configuration. See the
539 [sample configuration](https://github.com/element-hq/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/sample_config.yaml)
542 Secrets should be passed in by using the `extraConfigFiles` option.
544 type = with types; submodule {
545 freeformType = format.type;
547 # This is a reduced set of popular options and defaults
548 # Do not add every available option here, they can be specified
549 # by the user at their own discretion. This is a freeform type!
551 server_name = mkOption {
553 example = "example.com";
554 default = config.networking.hostName;
555 defaultText = literalExpression "config.networking.hostName";
557 The domain name of the server, with optional explicit port.
558 This is used by remote servers to look up the server address.
559 This is also the last part of your UserID.
561 The server_name cannot be changed later so it is important to configure this correctly before you start Synapse.
565 enable_registration = mkOption {
569 Enable registration for new users.
573 registration_shared_secret = mkOption {
574 type = types.nullOr types.str;
577 If set, allows registration by anyone who also has the shared
578 secret, even if registration is otherwise disabled.
580 Secrets should be passed in via `extraConfigFiles`!
584 macaroon_secret_key = mkOption {
585 type = types.nullOr types.str;
588 Secret key for authentication tokens. If none is specified,
589 the registration_shared_secret is used, if one is given; otherwise,
590 a secret key is derived from the signing key.
592 Secrets should be passed in via `extraConfigFiles`!
596 enable_metrics = mkOption {
600 Enable collection and rendering of performance metrics
604 report_stats = mkOption {
608 Whether or not to report anonymized homeserver usage statistics.
612 signing_key_path = mkOption {
614 default = "${cfg.dataDir}/homeserver.signing.key";
616 Path to the signing key to sign messages with.
620 pid_file = mkOption {
622 default = "/run/matrix-synapse.pid";
625 The file to store the PID in.
629 log_config = mkOption {
631 default = genLogConfigFile "synapse";
632 defaultText = logConfigText "synapse";
634 The file that holds the logging configuration.
638 media_store_path = mkOption {
640 default = if lib.versionAtLeast config.system.stateVersion "22.05"
641 then "${cfg.dataDir}/media_store"
642 else "${cfg.dataDir}/media";
643 defaultText = "${cfg.dataDir}/media_store for when system.stateVersion is at least 22.05, ${cfg.dataDir}/media when lower than 22.05";
645 Directory where uploaded images and attachments are stored.
649 public_baseurl = mkOption {
650 type = types.nullOr types.str;
652 example = "https://example.com:8448/";
654 The public-facing base URL for the client API (not including _matrix/...)
658 tls_certificate_path = mkOption {
659 type = types.nullOr types.str;
661 example = "/var/lib/acme/example.com/fullchain.pem";
663 PEM encoded X509 certificate for TLS.
664 You can replace the self-signed certificate that synapse
665 autogenerates on launch with your own SSL certificate + key pair
666 if you like. Any required intermediary certificates can be
667 appended after the primary certificate in hierarchical order.
671 tls_private_key_path = mkOption {
672 type = types.nullOr types.str;
674 example = "/var/lib/acme/example.com/key.pem";
676 PEM encoded private key for TLS. Specify null if synapse is not
677 speaking TLS directly.
681 presence.enabled = mkOption {
686 Whether to enable presence tracking.
688 Presence tracking allows users to see the state (e.g online/offline)
689 of other local and remote users.
693 listeners = mkOption {
694 type = types.listOf (listenerType false);
697 bind_addresses = [ "127.0.0.1" ];
702 names = [ "client" ];
705 names = [ "federation" ];
708 }] ++ lib.optional hasWorkers {
709 path = "/run/matrix-synapse/main_replication.sock";
712 names = [ "replication" ];
717 List of ports that Synapse should listen on, their purpose and their configuration.
719 By default, synapse will be configured for client and federation traffic on port 8008, and
720 use a UNIX domain socket for worker replication. See [`services.matrix-synapse.workers`](#opt-services.matrix-synapse.workers)
725 database.name = mkOption {
730 default = if versionAtLeast config.system.stateVersion "18.03"
733 defaultText = literalExpression ''
734 if versionAtLeast config.system.stateVersion "18.03"
739 The database engine name. Can be sqlite3 or psycopg2.
743 database.args.database = mkOption {
746 sqlite3 = "${cfg.dataDir}/homeserver.db";
747 psycopg2 = "matrix-synapse";
748 }.${cfg.settings.database.name};
749 defaultText = literalExpression ''
751 sqlite3 = "''${${options.services.matrix-synapse.dataDir}}/homeserver.db";
752 psycopg2 = "matrix-synapse";
753 }.''${${options.services.matrix-synapse.settings}.database.name};
756 Name of the database when using the psycopg2 backend,
757 path to the database location when using sqlite3.
761 database.args.user = mkOption {
762 type = types.nullOr types.str;
765 psycopg2 = "matrix-synapse";
766 }.${cfg.settings.database.name};
767 defaultText = lib.literalExpression ''
770 psycopg2 = "matrix-synapse";
771 }.''${cfg.settings.database.name};
774 Username to connect with psycopg2, set to null
779 url_preview_enabled = mkOption {
784 Is the preview URL API enabled? If enabled, you *must* specify an
785 explicit url_preview_ip_range_blacklist of IPs that the spider is
786 denied from accessing.
790 url_preview_ip_range_blacklist = mkOption {
791 type = types.listOf types.str;
814 List of IP address CIDR ranges that the URL preview spider is denied
819 url_preview_ip_range_whitelist = mkOption {
820 type = types.listOf types.str;
823 List of IP address CIDR ranges that the URL preview spider is allowed
824 to access even if they are specified in url_preview_ip_range_blacklist.
828 url_preview_url_blacklist = mkOption {
829 # FIXME revert to just `listOf (attrsOf str)` after some time(tm).
830 type = types.listOf (
834 Setting `config.services.matrix-synapse.settings.url_preview_url_blacklist`
835 to a list of strings has never worked. Due to a bug, this was the type accepted
836 by the module, but in practice it broke on runtime and as a result, no URL
837 preview worked anywhere if this was set.
839 See https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#url_preview_url_blacklist
840 on how to configure it properly.
842 (types.attrsOf types.str));
844 example = literalExpression ''
846 { scheme = "http"; } # no http previews
847 { netloc = "www.acme.com"; path = "/foo"; } # block http(s)://www.acme.com/foo
851 Optional list of URL matches that the URL preview spider is
852 denied from accessing.
856 max_upload_size = mkOption {
861 The largest allowed upload size in bytes
865 max_image_pixels = mkOption {
870 Maximum number of pixels that will be thumbnailed
874 dynamic_thumbnails = mkOption {
879 Whether to generate new thumbnails on the fly to precisely match
880 the resolution requested by the client. If true then whenever
881 a new resolution is requested by the client the server will
882 generate a new thumbnail. If false the server will pick a thumbnail
883 from a precalculated list.
887 turn_uris = mkOption {
888 type = types.listOf types.str;
891 "turn:turn.example.com:3487?transport=udp"
892 "turn:turn.example.com:3487?transport=tcp"
893 "turns:turn.example.com:5349?transport=udp"
894 "turns:turn.example.com:5349?transport=tcp"
897 The public URIs of the TURN server to give to clients
900 turn_shared_secret = mkOption {
903 example = literalExpression ''
904 config.services.coturn.static-auth-secret
907 The shared secret used to compute passwords for the TURN server.
909 Secrets should be passed in via `extraConfigFiles`!
913 trusted_key_servers = mkOption {
914 type = types.listOf (types.submodule {
915 freeformType = format.type;
917 server_name = mkOption {
919 example = "matrix.org";
921 Hostname of the trusted server.
927 server_name = "matrix.org";
929 "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
933 The trusted servers to download signing keys from.
937 app_service_config_files = mkOption {
938 type = types.listOf types.path;
941 A list of application service config file to use
945 redis = lib.mkOption {
946 type = types.submodule {
947 freeformType = format.type;
949 enabled = lib.mkOption {
953 Whether to use redis support
960 Redis configuration for synapse.
963 [upstream documentation](https://github.com/element-hq/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/usage/configuration/config_documentation.md#redis)
964 for available options.
971 workers = lib.mkOption {
974 Options for configuring workers. Worker support will be enabled if at least one worker is configured here.
976 See the [worker documention](https://element-hq.github.io/synapse/latest/workers.html#worker-configuration)
977 for possible options for each worker. Worker-specific options overriding the shared homeserver configuration can be
978 specified here for each worker.
981 Worker support will add a replication listener on port 9093 to the main synapse process using the default
982 value of [`services.matrix-synapse.settings.listeners`](#opt-services.matrix-synapse.settings.listeners) and configure that
983 listener as `services.matrix-synapse.settings.instance_map.main`.
984 If you set either of those options, make sure to configure a replication listener yourself.
986 A redis server is required for running workers. A local one can be enabled
987 using [`services.matrix-synapse.configureRedisLocally`](#opt-services.matrix-synapse.configureRedisLocally).
989 Workers also require a proper reverse proxy setup to direct incoming requests to the appropriate process. See
990 the [reverse proxy documentation](https://element-hq.github.io/synapse/latest/reverse_proxy.html) for a
991 general reverse proxying setup and
992 the [worker documentation](https://element-hq.github.io/synapse/latest/workers.html#available-worker-applications)
993 for the available endpoints per worker application.
996 type = types.attrsOf (types.submodule ({name, ...}: {
997 freeformType = format.type;
999 worker_app = lib.mkOption {
1001 "synapse.app.generic_worker"
1002 "synapse.app.media_repository"
1004 description = "Type of this worker";
1005 default = "synapse.app.generic_worker";
1007 worker_listeners = lib.mkOption {
1009 type = types.listOf (listenerType true);
1011 List of ports that this worker should listen on, their purpose and their configuration.
1014 worker_log_config = lib.mkOption {
1016 default = genLogConfigFile "synapse-${name}";
1017 defaultText = logConfigText "synapse-${name}";
1019 The file for log configuration.
1021 See the [python documentation](https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema)
1022 for the schema and the [upstream repository](https://github.com/element-hq/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/sample_log_config.yaml)
1029 example = lib.literalExpression ''
1031 "federation_sender" = { };
1032 "federation_receiver" = {
1033 worker_listeners = [
1037 bind_addresses = [ "127.0.0.1" ];
1041 names = [ "federation" ];
1050 extraConfigFiles = mkOption {
1051 type = types.listOf types.path;
1054 Extra config files to include.
1056 The configuration files will be included based on the command line
1057 argument --config-path. This allows to configure secrets without
1058 having to go through the Nix store, e.g. based on deployment keys if
1063 configureRedisLocally = lib.mkOption {
1067 Whether to automatically configure a local redis server for matrix-synapse.
1073 config = mkIf cfg.enable {
1076 assertion = clientListener != null;
1078 At least one listener which serves the `client` resource via HTTP is required
1079 by synapse in `services.matrix-synapse.settings.listeners` or in one of the workers!
1083 assertion = hasWorkers -> cfg.settings.redis.enabled;
1085 Workers for matrix-synapse require configuring a redis instance. This can be done
1086 automatically by setting `services.matrix-synapse.configureRedisLocally = true`.
1092 main = cfg.settings.instance_map.main;
1093 listener = lib.findFirst
1097 lib.hasAttr "port" main && listener.port or null == main.port
1098 || lib.hasAttr "path" main && listener.path or null == main.path
1100 && listenerSupportsResource "replication" listener
1102 lib.hasAttr "host" main && lib.any (bind: bind == main.host || bind == "0.0.0.0" || bind == "::") listener.bind_addresses
1103 || lib.hasAttr "path" main
1107 cfg.settings.listeners;
1109 hasWorkers -> (cfg.settings.instance_map ? main && listener != null);
1111 Workers for matrix-synapse require setting `services.matrix-synapse.settings.instance_map.main`
1112 to any listener configured in `services.matrix-synapse.settings.listeners` with a `"replication"`
1115 This is done by default unless you manually configure either of those settings.
1119 assertion = cfg.enableRegistrationScript -> clientListener.path == null;
1121 The client listener on matrix-synapse is configured to use UNIX domain sockets.
1122 This configuration is incompatible with the `register_new_matrix_user` script.
1124 Disable `services.matrix-synapse.enableRegistrationScript` to continue.
1128 ++ (map (listener: {
1129 assertion = (listener.path == null) != (listener.bind_addresses == null);
1131 Listeners require either a UNIX domain socket `path` or `bind_addresses` for a TCP socket.
1133 }) cfg.settings.listeners)
1134 ++ (map (listener: {
1135 assertion = listener.path != null -> (listener.bind_addresses == null && listener.port == null && listener.tls == null);
1137 formatKeyValue = key: value: lib.optionalString (value != null) " - ${key}=${toString value}\n";
1139 Listener configured with UNIX domain socket (${toString listener.path}) ignores the following options:
1140 ${formatKeyValue "bind_addresses" listener.bind_addresses}${formatKeyValue "port" listener.port}${formatKeyValue "tls" listener.tls}
1142 }) cfg.settings.listeners)
1143 ++ (map (listener: {
1144 assertion = listener.path == null || listener.type == "http";
1146 Listener configured with UNIX domain socket (${toString listener.path}) only supports the "http" listener type.
1148 }) cfg.settings.listeners);
1150 services.matrix-synapse.settings.redis = lib.mkIf cfg.configureRedisLocally {
1152 path = config.services.redis.servers.matrix-synapse.unixSocket;
1154 services.matrix-synapse.settings.instance_map.main = lib.mkIf hasWorkers (lib.mkDefault {
1155 path = "/run/matrix-synapse/main_replication.sock";
1158 services.matrix-synapse.serviceUnit = if hasWorkers then "matrix-synapse.target" else "matrix-synapse.service";
1159 services.matrix-synapse.configFile = configFile;
1160 services.matrix-synapse.package = wrapped;
1162 # default them, so they are additive
1163 services.matrix-synapse.extras = defaultExtras;
1165 services.matrix-synapse.log = mapAttrsRecursive (const mkDefault) defaultCommonLogConfig;
1167 users.users.matrix-synapse = {
1168 group = "matrix-synapse";
1171 shell = "${pkgs.bash}/bin/bash";
1172 uid = config.ids.uids.matrix-synapse;
1175 users.groups.matrix-synapse = {
1176 gid = config.ids.gids.matrix-synapse;
1179 systemd.targets.matrix-synapse = lib.mkIf hasWorkers {
1180 description = "Synapse Matrix parent target";
1181 wants = [ "network-online.target" ];
1182 after = [ "network-online.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
1183 wantedBy = [ "multi-user.target" ];
1191 partOf = [ "matrix-synapse.target" ];
1192 wantedBy = [ "matrix-synapse.target" ];
1193 unitConfig.ReloadPropagatedFrom = "matrix-synapse.target";
1194 requires = optional hasLocalPostgresDB "postgresql.service";
1197 wants = [ "network-online.target" ];
1198 after = [ "network-online.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
1199 requires = optional hasLocalPostgresDB "postgresql.service";
1200 wantedBy = [ "multi-user.target" ];
1202 baseServiceConfig = {
1203 environment = optionalAttrs (cfg.withJemalloc) {
1204 LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so";
1208 User = "matrix-synapse";
1209 Group = "matrix-synapse";
1210 WorkingDirectory = cfg.dataDir;
1211 RuntimeDirectory = "matrix-synapse";
1212 RuntimeDirectoryPreserve = true;
1213 ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
1214 Restart = "on-failure";
1217 # Security Hardening
1218 # Refer to systemd.exec(5) for option descriptions.
1219 CapabilityBoundingSet = [ "" ];
1220 LockPersonality = true;
1221 NoNewPrivileges = true;
1222 PrivateDevices = true;
1224 PrivateUsers = true;
1226 ProtectClock = true;
1227 ProtectControlGroups = true;
1229 ProtectHostname = true;
1230 ProtectKernelLogs = true;
1231 ProtectKernelModules = true;
1232 ProtectKernelTunables = true;
1233 ProtectProc = "invisible";
1234 ProtectSystem = "strict";
1235 ReadWritePaths = [ cfg.dataDir cfg.settings.media_store_path ] ++
1236 (map (listener: dirOf listener.path) (filter (listener: listener.path != null) cfg.settings.listeners));
1238 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
1239 RestrictNamespaces = true;
1240 RestrictRealtime = true;
1241 RestrictSUIDSGID = true;
1242 SystemCallArchitectures = "native";
1243 SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ];
1247 genWorkerService = name: workerCfg:
1249 finalWorkerCfg = workerCfg // { worker_name = name; };
1250 workerConfigFile = format.generate "worker-${name}.yaml" finalWorkerCfg;
1253 name = "matrix-synapse-worker-${name}";
1254 value = lib.mkMerge [
1257 description = "Synapse Matrix worker ${name}";
1258 # make sure the main process starts first for potential database migrations
1259 after = [ "matrix-synapse.service" ];
1260 requires = [ "matrix-synapse.service" ];
1263 ${cfg.package}/bin/synapse_worker \
1264 ${ concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ configFile workerConfigFile ] ++ cfg.extraConfigFiles) }
1265 --keys-directory ${cfg.dataDir}
1273 matrix-synapse = lib.mkMerge [
1276 description = "Synapse Matrix homeserver";
1278 ${cfg.package}/bin/synapse_homeserver \
1279 --config-path ${configFile} \
1280 --keys-directory ${cfg.dataDir} \
1285 ("+" + (pkgs.writeShellScript "matrix-synapse-fix-permissions" ''
1286 chown matrix-synapse:matrix-synapse ${cfg.settings.signing_key_path}
1287 chmod 0600 ${cfg.settings.signing_key_path}
1291 ${cfg.package}/bin/synapse_homeserver \
1292 ${ concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) }
1293 --keys-directory ${cfg.dataDir}
1299 // (lib.mapAttrs' genWorkerService cfg.workers);
1301 services.redis.servers.matrix-synapse = lib.mkIf cfg.configureRedisLocally {
1303 user = "matrix-synapse";
1306 environment.systemPackages = lib.optionals cfg.enableRegistrationScript [
1307 registerNewMatrixUser
1312 buildDocsInSandbox = false;
1314 maintainers = teams.matrix.members;