Self-host your own digital island
1{ config, pkgs, lib, ... }:
2
3let cfg = config.eilean; in
4{
5 options.eilean.matrix = {
6 enable = lib.mkEnableOption "matrix";
7 turn = lib.mkOption {
8 type = lib.types.bool;
9 default = true;
10 };
11 };
12
13 config = lib.mkIf cfg.matrix.enable {
14 services.postgresql.enable = true;
15 services.postgresql.package = pkgs.postgresql_13;
16 services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
17 CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
18 CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
19 TEMPLATE template0
20 LC_COLLATE = "C"
21 LC_CTYPE = "C";
22 '';
23
24 services.nginx = {
25 enable = true;
26 # only recommendedProxySettings and recommendedGzipSettings are strictly required,
27 # but the rest make sense as well
28 recommendedTlsSettings = true;
29 recommendedOptimisation = true;
30 recommendedGzipSettings = true;
31 recommendedProxySettings = true;
32
33 virtualHosts = {
34 # This host section can be placed on a different host than the rest,
35 # i.e. to delegate from the host being accessible as ${config.networking.domain}
36 # to another host actually running the Matrix homeserver.
37 "${config.networking.domain}" = {
38 enableACME = true;
39 forceSSL = true;
40
41 locations."= /.well-known/matrix/server".extraConfig =
42 let
43 # use 443 instead of the default 8448 port to unite
44 # the client-server and server-server port for simplicity
45 server = { "m.server" = "matrix.${config.networking.domain}:443"; };
46 in ''
47 add_header Content-Type application/json;
48 return 200 '${builtins.toJSON server}';
49 '';
50 locations."= /.well-known/matrix/client".extraConfig =
51 let
52 client = {
53 "m.homeserver" = { "base_url" = "https://matrix.${config.networking.domain}"; };
54 "m.identity_server" = { "base_url" = "https://vector.im"; };
55 };
56 # ACAO required to allow element-web on any URL to request this json file
57 in ''
58 add_header Content-Type application/json;
59 add_header Access-Control-Allow-Origin *;
60 return 200 '${builtins.toJSON client}';
61 '';
62 };
63
64 # Reverse proxy for Matrix client-server and server-server communication
65 "matrix.${config.networking.domain}" = {
66 enableACME = true;
67 forceSSL = true;
68
69 # Or do a redirect instead of the 404, or whatever is appropriate for you.
70 # But do not put a Matrix Web client here! See the Element web section below.
71 locations."/".extraConfig = ''
72 return 404;
73 '';
74
75 # forward all Matrix API calls to the synapse Matrix homeserver
76 locations."/_matrix" = {
77 proxyPass = "http://127.0.0.1:8008"; # without a trailing /
78 #proxyPassReverse = "http://127.0.0.1:8008"; # without a trailing /
79 };
80 };
81 };
82 };
83
84 services.matrix-synapse = {
85 enable = true;
86 settings = lib.mkMerge [
87 {
88 server_name = config.networking.domain;
89 enable_registration = true;
90 registration_requires_token = true;
91 auto_join_rooms = [ "#freumh:freumh.org" ];
92 registration_shared_secret_path = "${config.eilean.secretsDir}/matrix-shared-secret";
93 listeners = [
94 {
95 port = 8008;
96 bind_addresses = [ "::1" "127.0.0.1" ];
97 type = "http";
98 tls = false;
99 x_forwarded = true;
100 resources = [
101 {
102 names = [ "client" "federation" ];
103 compress = false;
104 }
105 ];
106 }
107 ];
108 max_upload_size = "100M";
109 }
110 (lib.mkIf cfg.matrix.turn {
111 turn_uris = with config.services.coturn; [
112 "turn:${realm}:3478?transport=udp"
113 "turn:${realm}:3478?transport=tcp"
114 "turns:${realm}:5349?transport=udp"
115 "turns:${realm}:5349?transport=tcp"
116 ];
117 turn_user_lifetime = "1h";
118 })
119 ];
120 extraConfigFiles = [ "${config.eilean.secretsDir}/matrix-turn-shared-secret" ];
121 };
122
123 eilean.turn.enable = lib.mkIf cfg.matrix.turn true;
124
125 eilean.dns.enable = true;
126 eilean.services.dns.zones.${config.networking.domain}.records = [
127 {
128 name = "matrix";
129 type = "CNAME";
130 data = "vps";
131 }
132 ];
133 };
134}