]> git.immae.eu Git - perso/Immae/Config/Nix.git/blame - modules/webapps/mastodon.nix
Add dry-run patch for morph
[perso/Immae/Config/Nix.git] / modules / webapps / mastodon.nix
CommitLineData
613aea56
IB
1{ lib, pkgs, config, ... }:
2let
3 name = "mastodon";
4 cfg = config.services.mastodon;
5
6 uid = config.ids.uids.mastodon;
7 gid = config.ids.gids.mastodon;
8in
9{
10 options.services.mastodon = {
11 enable = lib.mkEnableOption "Enable Mastodon’s service";
12 user = lib.mkOption {
13 type = lib.types.str;
14 default = name;
15 description = "User account under which Mastodon runs";
16 };
17 group = lib.mkOption {
18 type = lib.types.str;
19 default = name;
20 description = "Group under which Mastodon runs";
21 };
22 dataDir = lib.mkOption {
23 type = lib.types.path;
24 default = "/var/lib/${name}";
25 description = ''
26 The directory where Mastodon stores its data.
27 '';
28 };
29 socketsPrefix = lib.mkOption {
5400b9b6 30 type = lib.types.str;
613aea56
IB
31 default = "live";
32 description = ''
33 The prefix to use for Mastodon sockets.
34 '';
35 };
36 socketsDir = lib.mkOption {
37 type = lib.types.path;
38 default = "/run/${name}";
39 description = ''
40 The directory where Mastodon puts runtime files and sockets.
41 '';
42 };
43 configFile = lib.mkOption {
44 type = lib.types.path;
45 description = ''
46 The configuration file path for Mastodon.
47 '';
48 };
49 package = lib.mkOption {
50 type = lib.types.package;
51 default = pkgs.webapps.mastodon;
52 description = ''
53 Mastodon package to use.
54 '';
55 };
56 # Output variables
57 workdir = lib.mkOption {
58 type = lib.types.package;
59 default = cfg.package.override { varDir = cfg.dataDir; };
60 description = ''
61 Adjusted mastodon package with overriden varDir
62 '';
63 readOnly = true;
64 };
81b9ff89
IB
65 systemdStateDirectory = lib.mkOption {
66 type = lib.types.str;
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;
70 description = ''
71 Adjusted Mastodon data directory for systemd
72 '';
73 readOnly = true;
74 };
75 systemdRuntimeDirectory = lib.mkOption {
76 type = lib.types.str;
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;
80 description = ''
81 Adjusted Mastodon sockets directory for systemd
82 '';
83 readOnly = true;
84 };
613aea56
IB
85 sockets = lib.mkOption {
86 type = lib.types.attrsOf lib.types.path;
87 default = {
88 node = "${cfg.socketsDir}/${cfg.socketsPrefix}_node.sock";
89 rails = "${cfg.socketsDir}/${cfg.socketsPrefix}_puma.sock";
90 };
91 readOnly = true;
92 description = ''
93 Mastodon sockets
94 '';
95 };
96 };
97
98 config = lib.mkIf cfg.enable {
258dd18b
IB
99 users.users = lib.optionalAttrs (cfg.user == name) {
100 "${name}" = {
101 inherit uid;
102 group = cfg.group;
103 description = "Mastodon user";
104 home = cfg.dataDir;
105 useDefaultShell = true;
106 };
107 };
108 users.groups = lib.optionalAttrs (cfg.group == name) {
109 "${name}" = {
110 inherit gid;
111 };
112 };
613aea56 113
850adcf4
IB
114 systemd.slices.mastodon = {
115 description = "Mastodon slice";
116 };
117
613aea56
IB
118 systemd.services.mastodon-streaming = {
119 description = "Mastodon Streaming";
120 wantedBy = [ "multi-user.target" ];
121 after = [ "network.target" "mastodon-web.service" ];
122
123 environment.NODE_ENV = "production";
124 environment.SOCKET = cfg.sockets.node;
125
126 path = [ pkgs.nodejs pkgs.bashInteractive ];
127
128 script = ''
129 exec npm run start
130 '';
131
132 postStart = ''
133 while [ ! -S $SOCKET ]; do
134 sleep 0.5
135 done
136 chmod a+w $SOCKET
137 '';
138
139 postStop = ''
140 rm $SOCKET
141 '';
142
143 serviceConfig = {
850adcf4 144 Slice = "mastodon.slice";
613aea56
IB
145 User = cfg.user;
146 EnvironmentFile = cfg.configFile;
147 PrivateTmp = true;
148 Restart = "always";
149 TimeoutSec = 15;
150 Type = "simple";
151 WorkingDirectory = cfg.workdir;
81b9ff89
IB
152 StateDirectory = cfg.systemdStateDirectory;
153 RuntimeDirectory = cfg.systemdRuntimeDirectory;
154 RuntimeDirectoryPreserve = "yes";
613aea56
IB
155 };
156
157 unitConfig.RequiresMountsFor = cfg.dataDir;
158 };
159
160 systemd.services.mastodon-web = {
161 description = "Mastodon Web app";
162 wantedBy = [ "multi-user.target" ];
163 after = [ "network.target" ];
164
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;
169
170 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file ];
171
172 preStart = ''
2a5cde8d 173 install -m 0755 -d ${cfg.dataDir}/tmp/cache
613aea56
IB
174 ./bin/bundle exec rails db:migrate
175 '';
176
177 script = ''
178 exec ./bin/bundle exec puma -C config/puma.rb
179 '';
180
6551e648
IB
181 postStart = ''
182 exec ./bin/tootctl cache clear
183 '';
613aea56 184 serviceConfig = {
850adcf4 185 Slice = "mastodon.slice";
613aea56
IB
186 User = cfg.user;
187 EnvironmentFile = cfg.configFile;
188 PrivateTmp = true;
189 Restart = "always";
190 TimeoutSec = 60;
191 Type = "simple";
192 WorkingDirectory = cfg.workdir;
81b9ff89
IB
193 StateDirectory = cfg.systemdStateDirectory;
194 RuntimeDirectory = cfg.systemdRuntimeDirectory;
195 RuntimeDirectoryPreserve = "yes";
613aea56
IB
196 };
197
198 unitConfig.RequiresMountsFor = cfg.dataDir;
199 };
200
6a8252b1
IB
201 systemd.services.mastodon-cleanup = {
202 description = "Cleanup mastodon";
203 startAt = "daily";
204 restartIfChanged = false;
205
206 environment.RAILS_ENV = "production";
207 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
208 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
209 environment.SOCKET = cfg.sockets.rails;
210
211 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file ];
212
213 script = ''
214 exec ./bin/tootctl media remove --days 30
215 '';
216
217 serviceConfig = {
218 User = cfg.user;
219 EnvironmentFile = cfg.configFile;
220 PrivateTmp = true;
221 Type = "oneshot";
222 WorkingDirectory = cfg.workdir;
223 StateDirectory = cfg.systemdStateDirectory;
224 RuntimeDirectory = cfg.systemdRuntimeDirectory;
225 RuntimeDirectoryPreserve = "yes";
226 };
227
228 unitConfig.RequiresMountsFor = cfg.dataDir;
229 };
230
613aea56
IB
231 systemd.services.mastodon-sidekiq = {
232 description = "Mastodon Sidekiq";
233 wantedBy = [ "multi-user.target" ];
234 after = [ "network.target" "mastodon-web.service" ];
235
236 environment.RAILS_ENV="production";
237 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
238 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
239 environment.DB_POOL="5";
240
241 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.imagemagick pkgs.ffmpeg pkgs.file ];
242
243 script = ''
244 exec ./bin/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
245 '';
246
247 serviceConfig = {
850adcf4 248 Slice = "mastodon.slice";
613aea56
IB
249 User = cfg.user;
250 EnvironmentFile = cfg.configFile;
251 PrivateTmp = true;
252 Restart = "always";
253 TimeoutSec = 15;
254 Type = "simple";
255 WorkingDirectory = cfg.workdir;
81b9ff89
IB
256 StateDirectory = cfg.systemdStateDirectory;
257 RuntimeDirectory = cfg.systemdRuntimeDirectory;
258 RuntimeDirectoryPreserve = "yes";
613aea56
IB
259 };
260
261 unitConfig.RequiresMountsFor = cfg.dataDir;
262 };
263
613aea56
IB
264 };
265}