]>
Commit | Line | Data |
---|---|---|
613aea56 IB |
1 | { lib, pkgs, config, ... }: |
2 | let | |
3 | name = "mastodon"; | |
4 | cfg = config.services.mastodon; | |
5 | ||
6 | uid = config.ids.uids.mastodon; | |
7 | gid = config.ids.gids.mastodon; | |
8 | in | |
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 | ||
f0255dd5 | 170 | path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file pkgs.imagemagick ]; |
613aea56 IB |
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 | ||
18eb98cf IB |
201 | # To be run manually because computationnally heavy |
202 | systemd.services.mastodon-cleanup-manual = { | |
203 | description = "Cleanup mastodon"; | |
204 | ||
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; | |
209 | ||
210 | path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file ]; | |
211 | ||
212 | script = '' | |
213 | exec ./bin/tootctl statuses remove --days 365 | |
214 | ''; | |
215 | ||
216 | serviceConfig = { | |
217 | User = cfg.user; | |
218 | EnvironmentFile = cfg.configFile; | |
219 | PrivateTmp = true; | |
220 | Type = "oneshot"; | |
221 | WorkingDirectory = cfg.workdir; | |
222 | StateDirectory = cfg.systemdStateDirectory; | |
223 | RuntimeDirectory = cfg.systemdRuntimeDirectory; | |
224 | RuntimeDirectoryPreserve = "yes"; | |
225 | }; | |
226 | ||
227 | unitConfig.RequiresMountsFor = cfg.dataDir; | |
228 | }; | |
229 | ||
6a8252b1 IB |
230 | systemd.services.mastodon-cleanup = { |
231 | description = "Cleanup mastodon"; | |
232 | startAt = "daily"; | |
233 | restartIfChanged = false; | |
234 | ||
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; | |
239 | ||
240 | path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file ]; | |
241 | ||
242 | script = '' | |
243 | exec ./bin/tootctl media remove --days 30 | |
244 | ''; | |
245 | ||
246 | serviceConfig = { | |
247 | User = cfg.user; | |
248 | EnvironmentFile = cfg.configFile; | |
249 | PrivateTmp = true; | |
250 | Type = "oneshot"; | |
251 | WorkingDirectory = cfg.workdir; | |
252 | StateDirectory = cfg.systemdStateDirectory; | |
253 | RuntimeDirectory = cfg.systemdRuntimeDirectory; | |
254 | RuntimeDirectoryPreserve = "yes"; | |
255 | }; | |
256 | ||
257 | unitConfig.RequiresMountsFor = cfg.dataDir; | |
258 | }; | |
259 | ||
613aea56 IB |
260 | systemd.services.mastodon-sidekiq = { |
261 | description = "Mastodon Sidekiq"; | |
262 | wantedBy = [ "multi-user.target" ]; | |
263 | after = [ "network.target" "mastodon-web.service" ]; | |
264 | ||
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"; | |
269 | ||
270 | path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.imagemagick pkgs.ffmpeg pkgs.file ]; | |
271 | ||
272 | script = '' | |
273 | exec ./bin/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push | |
274 | ''; | |
275 | ||
276 | serviceConfig = { | |
850adcf4 | 277 | Slice = "mastodon.slice"; |
613aea56 IB |
278 | User = cfg.user; |
279 | EnvironmentFile = cfg.configFile; | |
280 | PrivateTmp = true; | |
281 | Restart = "always"; | |
282 | TimeoutSec = 15; | |
283 | Type = "simple"; | |
284 | WorkingDirectory = cfg.workdir; | |
81b9ff89 IB |
285 | StateDirectory = cfg.systemdStateDirectory; |
286 | RuntimeDirectory = cfg.systemdRuntimeDirectory; | |
287 | RuntimeDirectoryPreserve = "yes"; | |
613aea56 IB |
288 | }; |
289 | ||
290 | unitConfig.RequiresMountsFor = cfg.dataDir; | |
291 | }; | |
292 | ||
613aea56 IB |
293 | }; |
294 | } |