1 { lib, pkgs, config, ... }:
4 cfg = config.services.mastodon;
6 uid = config.ids.uids.mastodon;
7 gid = config.ids.gids.mastodon;
10 options.services.mastodon = {
11 enable = lib.mkEnableOption "Enable Mastodon’s service";
15 description = "User account under which Mastodon runs";
17 group = lib.mkOption {
20 description = "Group under which Mastodon runs";
22 dataDir = lib.mkOption {
23 type = lib.types.path;
24 default = "/var/lib/${name}";
26 The directory where Mastodon stores its data.
29 socketsPrefix = lib.mkOption {
33 The prefix to use for Mastodon sockets.
36 socketsDir = lib.mkOption {
37 type = lib.types.path;
38 default = "/run/${name}";
40 The directory where Mastodon puts runtime files and sockets.
43 configFile = lib.mkOption {
44 type = lib.types.path;
46 The configuration file path for Mastodon.
49 package = lib.mkOption {
50 type = lib.types.package;
51 default = pkgs.webapps.mastodon;
53 Mastodon package to use.
57 workdir = lib.mkOption {
58 type = lib.types.package;
59 default = cfg.package.override { varDir = cfg.dataDir; };
61 Adjusted mastodon package with overriden varDir
65 systemdStateDirectory = lib.mkOption {
67 # Use ReadWritePaths= instead if varDir is outside of /var/lib
68 default = assert lib.strings.hasPrefix "/var/lib/" cfg.dataDir;
69 lib.strings.removePrefix "/var/lib/" cfg.dataDir;
71 Adjusted Mastodon data directory for systemd
75 systemdRuntimeDirectory = lib.mkOption {
77 # Use ReadWritePaths= instead if socketsDir is outside of /run
78 default = assert lib.strings.hasPrefix "/run/" cfg.socketsDir;
79 lib.strings.removePrefix "/run/" cfg.socketsDir;
81 Adjusted Mastodon sockets directory for systemd
85 sockets = lib.mkOption {
86 type = lib.types.attrsOf lib.types.path;
88 node = "${cfg.socketsDir}/${cfg.socketsPrefix}_node.sock";
89 rails = "${cfg.socketsDir}/${cfg.socketsPrefix}_puma.sock";
98 config = lib.mkIf cfg.enable {
99 users.users = lib.optionalAttrs (cfg.user == name) {
103 description = "Mastodon user";
105 useDefaultShell = true;
108 users.groups = lib.optionalAttrs (cfg.group == name) {
114 systemd.slices.mastodon = {
115 description = "Mastodon slice";
118 systemd.services.mastodon-streaming = {
119 description = "Mastodon Streaming";
120 wantedBy = [ "multi-user.target" ];
121 after = [ "network.target" "mastodon-web.service" ];
123 environment.NODE_ENV = "production";
124 environment.SOCKET = cfg.sockets.node;
126 path = [ pkgs.nodejs pkgs.bashInteractive ];
133 while [ ! -S $SOCKET ]; do
144 Slice = "mastodon.slice";
146 EnvironmentFile = cfg.configFile;
151 WorkingDirectory = cfg.workdir;
152 StateDirectory = cfg.systemdStateDirectory;
153 RuntimeDirectory = cfg.systemdRuntimeDirectory;
154 RuntimeDirectoryPreserve = "yes";
157 unitConfig.RequiresMountsFor = cfg.dataDir;
160 systemd.services.mastodon-web = {
161 description = "Mastodon Web app";
162 wantedBy = [ "multi-user.target" ];
163 after = [ "network.target" ];
165 environment.RAILS_ENV = "production";
166 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
167 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
168 environment.SOCKET = cfg.sockets.rails;
170 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file pkgs.imagemagick ];
173 install -m 0755 -d ${cfg.dataDir}/tmp/cache
174 ./bin/bundle exec rails db:migrate
178 exec ./bin/bundle exec puma -C config/puma.rb
182 exec ./bin/tootctl cache clear
185 Slice = "mastodon.slice";
187 EnvironmentFile = cfg.configFile;
192 WorkingDirectory = cfg.workdir;
193 StateDirectory = cfg.systemdStateDirectory;
194 RuntimeDirectory = cfg.systemdRuntimeDirectory;
195 RuntimeDirectoryPreserve = "yes";
198 unitConfig.RequiresMountsFor = cfg.dataDir;
201 # To be run manually because computationnally heavy
202 systemd.services.mastodon-cleanup-manual = {
203 description = "Cleanup mastodon";
205 environment.RAILS_ENV = "production";
206 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
207 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
208 environment.SOCKET = cfg.sockets.rails;
210 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file ];
213 exec ./bin/tootctl statuses remove --days 365
218 EnvironmentFile = cfg.configFile;
221 WorkingDirectory = cfg.workdir;
222 StateDirectory = cfg.systemdStateDirectory;
223 RuntimeDirectory = cfg.systemdRuntimeDirectory;
224 RuntimeDirectoryPreserve = "yes";
227 unitConfig.RequiresMountsFor = cfg.dataDir;
230 systemd.services.mastodon-cleanup = {
231 description = "Cleanup mastodon";
233 restartIfChanged = false;
235 environment.RAILS_ENV = "production";
236 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
237 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
238 environment.SOCKET = cfg.sockets.rails;
240 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file ];
243 exec ./bin/tootctl media remove --days 30
248 EnvironmentFile = cfg.configFile;
251 WorkingDirectory = cfg.workdir;
252 StateDirectory = cfg.systemdStateDirectory;
253 RuntimeDirectory = cfg.systemdRuntimeDirectory;
254 RuntimeDirectoryPreserve = "yes";
257 unitConfig.RequiresMountsFor = cfg.dataDir;
260 systemd.services.mastodon-sidekiq = {
261 description = "Mastodon Sidekiq";
262 wantedBy = [ "multi-user.target" ];
263 after = [ "network.target" "mastodon-web.service" ];
265 environment.RAILS_ENV="production";
266 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
267 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
268 environment.DB_POOL="5";
270 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.imagemagick pkgs.ffmpeg pkgs.file ];
273 exec ./bin/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
277 Slice = "mastodon.slice";
279 EnvironmentFile = cfg.configFile;
284 WorkingDirectory = cfg.workdir;
285 StateDirectory = cfg.systemdStateDirectory;
286 RuntimeDirectory = cfg.systemdRuntimeDirectory;
287 RuntimeDirectoryPreserve = "yes";
290 unitConfig.RequiresMountsFor = cfg.dataDir;