1 { config, pkgs, lib, ... }:
7 cfg = config.services.mattermost;
9 database = "postgres://${cfg.localDatabaseUser}:${cfg.localDatabasePassword}@localhost:5432/${cfg.localDatabaseName}?sslmode=disable&connect_timeout=10";
11 postgresPackage = config.services.postgresql.package;
14 statePath ? cfg.statePath,
15 localDatabaseUser ? cfg.localDatabaseUser,
16 localDatabasePassword ? cfg.localDatabasePassword,
17 localDatabaseName ? cfg.localDatabaseName,
20 if ! test -e ${escapeShellArg "${statePath}/.db-created"}; then
21 ${lib.optionalString useSudo "${pkgs.sudo}/bin/sudo -u ${escapeShellArg config.services.postgresql.superUser} \\"}
22 ${postgresPackage}/bin/psql postgres -c \
23 "CREATE ROLE ${localDatabaseUser} WITH LOGIN NOCREATEDB NOCREATEROLE ENCRYPTED PASSWORD '${localDatabasePassword}'"
24 ${lib.optionalString useSudo "${pkgs.sudo}/bin/sudo -u ${escapeShellArg config.services.postgresql.superUser} \\"}
25 ${postgresPackage}/bin/createdb \
26 --owner ${escapeShellArg localDatabaseUser} ${escapeShellArg localDatabaseName}
27 touch ${escapeShellArg "${statePath}/.db-created"}
31 mattermostPluginDerivations = with pkgs;
32 map (plugin: stdenv.mkDerivation {
33 name = "mattermost-plugin";
36 cp ${plugin} $out/share/plugin.tar.gz
42 preferLocalBuild = true;
45 mattermostPlugins = with pkgs;
46 if mattermostPluginDerivations == [] then null
47 else stdenv.mkDerivation {
48 name = "${cfg.package.name}-plugins";
51 ] ++ mattermostPluginDerivations;
56 mkdir -p $out/data/plugins
57 plugins=(${escapeShellArgs (map (plugin: "${plugin}/share/plugin.tar.gz") mattermostPluginDerivations)})
58 for plugin in "''${plugins[@]}"; do
59 hash="$(sha256sum "$plugin" | cut -d' ' -f1)"
61 tar -C "$hash" -xzf "$plugin"
63 GZIP_OPT=-9 tar -C "$hash" -cvzf "$out/data/plugins/$hash.tar.gz" .
72 preferLocalBuild = true;
75 mattermostConfWithoutPlugins = recursiveUpdate
76 { ServiceSettings.SiteURL = cfg.siteUrl;
77 ServiceSettings.ListenAddress = cfg.listenAddress;
78 TeamSettings.SiteName = cfg.siteName;
79 SqlSettings.DriverName = "postgres";
80 SqlSettings.DataSource = database;
81 PluginSettings.Directory = "${cfg.statePath}/plugins/server";
82 PluginSettings.ClientDirectory = "${cfg.statePath}/plugins/client";
86 mattermostConf = recursiveUpdate
87 mattermostConfWithoutPlugins
89 lib.optionalAttrs (mattermostPlugins != null) {
96 mattermostConfJSON = pkgs.writeText "mattermost-config.json" (builtins.toJSON mattermostConf);
102 services.mattermost = {
103 enable = mkEnableOption "Mattermost chat server";
105 package = mkPackageOption pkgs "mattermost" { };
107 statePath = mkOption {
109 default = "/var/lib/mattermost";
110 description = "Mattermost working directory";
115 example = "https://chat.example.com";
117 URL this Mattermost instance is reachable under, without trailing slash.
121 siteName = mkOption {
123 default = "Mattermost";
124 description = "Name of this Mattermost site.";
127 listenAddress = mkOption {
130 example = "[::1]:8065";
132 Address and port this Mattermost instance listens to.
136 mutableConfig = mkOption {
140 Whether the Mattermost config.json is writeable by Mattermost.
142 Most of the settings can be edited in the system console of
143 Mattermost if this option is enabled. A template config using
144 the options specified in services.mattermost will be generated
145 but won't be overwritten on changes or rebuilds.
147 If this option is disabled, changes in the system console won't
148 be possible (default). If an config.json is present, it will be
153 preferNixConfig = mkOption {
157 If both mutableConfig and this option are set, the Nix configuration
158 will take precedence over any settings configured in the server
163 extraConfig = mkOption {
167 Additional configuration options as Nix attribute set in config.json schema.
172 type = types.listOf (types.oneOf [types.path types.package]);
174 example = "[ ./com.github.moussetc.mattermost.plugin.giphy-2.0.0.tar.gz ]";
176 Plugins to add to the configuration. Overrides any installed if non-null.
177 This is a list of paths to .tar.gz files or derivations evaluating to
181 environmentFile = mkOption {
182 type = types.nullOr types.path;
185 Environment file (see {manpage}`systemd.exec(5)`
186 "EnvironmentFile=" section for the syntax) which sets config options
187 for mattermost (see [the mattermost documentation](https://docs.mattermost.com/configure/configuration-settings.html#environment-variables)).
189 Settings defined in the environment file will overwrite settings
190 set via nix or via the {option}`services.mattermost.extraConfig`
193 Useful for setting config options without their value ending up in the
194 (world-readable) nix store, e.g. for a database password.
198 localDatabaseCreate = mkOption {
202 Create a local PostgreSQL database for Mattermost automatically.
206 localDatabaseName = mkOption {
208 default = "mattermost";
210 Local Mattermost database name.
214 localDatabaseUser = mkOption {
216 default = "mattermost";
218 Local Mattermost database username.
222 localDatabasePassword = mkOption {
224 default = "mmpgsecret";
226 Password for local Mattermost database user.
232 default = "mattermost";
234 User which runs the Mattermost service.
240 default = "mattermost";
242 Group which runs the Mattermost service.
247 enable = mkEnableOption "Mattermost IRC bridge";
248 package = mkPackageOption pkgs "matterircd" { };
249 parameters = mkOption {
250 type = types.listOf types.str;
252 example = [ "-mmserver chat.example.com" "-bind [::]:6667" ];
254 Set commandline parameters to pass to matterircd. See
255 https://github.com/42wim/matterircd#usage for more information.
264 users.users = optionalAttrs (cfg.user == "mattermost") {
267 uid = config.ids.uids.mattermost;
268 home = cfg.statePath;
272 users.groups = optionalAttrs (cfg.group == "mattermost") {
273 mattermost.gid = config.ids.gids.mattermost;
276 services.postgresql.enable = cfg.localDatabaseCreate;
278 # The systemd service will fail to execute the preStart hook
279 # if the WorkingDirectory does not exist
280 systemd.tmpfiles.settings."10-mattermost".${cfg.statePath}.d = { };
282 systemd.services.mattermost = {
283 description = "Mattermost chat service";
284 wantedBy = [ "multi-user.target" ];
285 after = [ "network.target" "postgresql.service" ];
288 mkdir -p "${cfg.statePath}"/{data,config,logs,plugins}
289 mkdir -p "${cfg.statePath}/plugins"/{client,server}
290 ln -sf ${cfg.package}/{bin,fonts,i18n,templates,client} "${cfg.statePath}"
291 '' + lib.optionalString (mattermostPlugins != null) ''
292 rm -rf "${cfg.statePath}/data/plugins"
293 ln -sf ${mattermostPlugins}/data/plugins "${cfg.statePath}/data"
294 '' + lib.optionalString (!cfg.mutableConfig) ''
295 rm -f "${cfg.statePath}/config/config.json"
296 ${pkgs.jq}/bin/jq -s '.[0] * .[1]' ${cfg.package}/config/config.json ${mattermostConfJSON} > "${cfg.statePath}/config/config.json"
297 '' + lib.optionalString cfg.mutableConfig ''
298 if ! test -e "${cfg.statePath}/config/.initial-created"; then
299 rm -f ${cfg.statePath}/config/config.json
300 ${pkgs.jq}/bin/jq -s '.[0] * .[1]' ${cfg.package}/config/config.json ${mattermostConfJSON} > "${cfg.statePath}/config/config.json"
301 touch "${cfg.statePath}/config/.initial-created"
303 '' + lib.optionalString (cfg.mutableConfig && cfg.preferNixConfig) ''
304 new_config="$(${pkgs.jq}/bin/jq -s '.[0] * .[1]' "${cfg.statePath}/config/config.json" ${mattermostConfJSON})"
306 rm -f "${cfg.statePath}/config/config.json"
307 echo "$new_config" > "${cfg.statePath}/config/config.json"
308 '' + lib.optionalString cfg.localDatabaseCreate (createDb {}) + ''
309 # Don't change permissions recursively on the data, current, and symlinked directories (see ln -sf command above).
310 # This dramatically decreases startup times for installations with a lot of files.
311 find . -maxdepth 1 -not -name data -not -name client -not -name templates -not -name i18n -not -name fonts -not -name bin -not -name . \
312 -exec chown "${cfg.user}:${cfg.group}" -R {} \; -exec chmod u+rw,g+r,o-rwx -R {} \;
314 chown "${cfg.user}:${cfg.group}" "${cfg.statePath}/data" .
315 chmod u+rw,g+r,o-rwx "${cfg.statePath}/data" .
319 PermissionsStartOnly = true;
322 ExecStart = "${cfg.package}/bin/mattermost";
323 WorkingDirectory = "${cfg.statePath}";
326 LimitNOFILE = "49152";
327 EnvironmentFile = cfg.environmentFile;
329 unitConfig.JoinsNamespaceOf = mkIf cfg.localDatabaseCreate "postgresql.service";
332 (mkIf cfg.matterircd.enable {
333 systemd.services.matterircd = {
334 description = "Mattermost IRC bridge service";
335 wantedBy = [ "multi-user.target" ];
339 ExecStart = "${cfg.matterircd.package}/bin/matterircd ${escapeShellArgs cfg.matterircd.parameters}";
340 WorkingDirectory = "/tmp";