]>
Commit | Line | Data |
---|---|---|
bc0f9fcf IB |
1 | { |
2 | description = "Your self-hosted, globally interconnected microblogging community"; | |
3 | inputs.myuids = { | |
4 | url = "https://git.immae.eu/perso/Immae/Config/Nix.git"; | |
5 | type = "git"; | |
6 | dir = "flakes/myuids"; | |
7 | }; | |
8 | inputs.flake-utils.url = "github:numtide/flake-utils"; | |
9 | inputs.nixpkgs = { | |
10 | url = "github:NixOS/nixpkgs/840c782d507d60aaa49aa9e3f6d0b0e780912742"; | |
11 | flake = false; | |
12 | }; | |
13 | inputs.mastodon = { | |
14 | url = "github:tootsuite/mastodon/v2.9.4"; | |
15 | flake = false; | |
16 | }; | |
17 | ||
18 | outputs = { self, myuids, nixpkgs, mastodon, flake-utils }: flake-utils.lib.eachSystem ["x86_64-linux"] (system: | |
19 | let | |
20 | pkgs = import nixpkgs { inherit system; overlays = []; }; | |
21 | version = (builtins.fromJSON (builtins.readFile ./flake.lock)).nodes.mastodon.original.ref; | |
22 | inherit (pkgs) callPackage; | |
23 | in rec { | |
24 | packages.mastodon = callPackage ./. { src = mastodon // { inherit version; }; }; | |
25 | defaultPackage = packages.mastodon; | |
26 | legacyPackages.mastodon = packages.mastodon; | |
27 | checks = { | |
28 | build = defaultPackage; | |
29 | }; | |
30 | } | |
31 | ) // rec { | |
32 | overlays = { | |
33 | mastodon = final: prev: { | |
34 | mastodon = self.defaultPackage."${final.system}"; | |
35 | }; | |
36 | }; | |
37 | overlay = overlays.mastodon; | |
38 | nixosModule = { lib, pkgs, config, ... }: | |
39 | let | |
40 | name = "mastodon"; | |
41 | cfg = config.immaeServices.mastodon; | |
42 | in | |
43 | { | |
44 | options.immaeServices.mastodon = { | |
45 | enable = lib.mkEnableOption "Enable Mastodon’s service"; | |
46 | user = lib.mkOption { | |
47 | type = lib.types.str; | |
48 | default = name; | |
49 | description = "User account under which Mastodon runs"; | |
50 | }; | |
51 | group = lib.mkOption { | |
52 | type = lib.types.str; | |
53 | default = name; | |
54 | description = "Group under which Mastodon runs"; | |
55 | }; | |
56 | dataDir = lib.mkOption { | |
57 | type = lib.types.path; | |
58 | default = "/var/lib/${name}"; | |
59 | description = '' | |
60 | The directory where Mastodon stores its data. | |
61 | ''; | |
62 | }; | |
63 | socketsPrefix = lib.mkOption { | |
64 | type = lib.types.str; | |
65 | default = "live"; | |
66 | description = '' | |
67 | The prefix to use for Mastodon sockets. | |
68 | ''; | |
69 | }; | |
70 | socketsDir = lib.mkOption { | |
71 | type = lib.types.path; | |
72 | default = "/run/${name}"; | |
73 | description = '' | |
74 | The directory where Mastodon puts runtime files and sockets. | |
75 | ''; | |
76 | }; | |
77 | configFile = lib.mkOption { | |
78 | type = lib.types.path; | |
79 | description = '' | |
80 | The configuration file path for Mastodon. | |
81 | ''; | |
82 | }; | |
83 | package = lib.mkOption { | |
84 | type = lib.types.package; | |
85 | default = pkgs.mastodon; | |
86 | description = '' | |
87 | Mastodon package to use. | |
88 | ''; | |
89 | }; | |
90 | # Output variables | |
91 | workdir = lib.mkOption { | |
92 | type = lib.types.package; | |
93 | default = cfg.package.override { varDir = cfg.dataDir; }; | |
94 | description = '' | |
95 | Adjusted mastodon package with overriden varDir | |
96 | ''; | |
97 | readOnly = true; | |
98 | }; | |
99 | systemdStateDirectory = lib.mkOption { | |
100 | type = lib.types.str; | |
101 | # Use ReadWritePaths= instead if varDir is outside of /var/lib | |
102 | default = assert lib.strings.hasPrefix "/var/lib/" cfg.dataDir; | |
103 | lib.strings.removePrefix "/var/lib/" cfg.dataDir; | |
104 | description = '' | |
105 | Adjusted Mastodon data directory for systemd | |
106 | ''; | |
107 | readOnly = true; | |
108 | }; | |
109 | systemdRuntimeDirectory = lib.mkOption { | |
110 | type = lib.types.str; | |
111 | # Use ReadWritePaths= instead if socketsDir is outside of /run | |
112 | default = assert lib.strings.hasPrefix "/run/" cfg.socketsDir; | |
113 | lib.strings.removePrefix "/run/" cfg.socketsDir; | |
114 | description = '' | |
115 | Adjusted Mastodon sockets directory for systemd | |
116 | ''; | |
117 | readOnly = true; | |
118 | }; | |
119 | sockets = lib.mkOption { | |
120 | type = lib.types.attrsOf lib.types.path; | |
121 | default = { | |
122 | node = "${cfg.socketsDir}/${cfg.socketsPrefix}_node.sock"; | |
123 | rails = "${cfg.socketsDir}/${cfg.socketsPrefix}_puma.sock"; | |
124 | }; | |
125 | readOnly = true; | |
126 | description = '' | |
127 | Mastodon sockets | |
128 | ''; | |
129 | }; | |
130 | }; | |
131 | ||
132 | config = lib.mkIf cfg.enable { | |
133 | nixpkgs.overlays = [ self.overlay ]; | |
134 | users.users = lib.optionalAttrs (cfg.user == name) { | |
135 | "${name}" = { | |
136 | uid = myuids.lib.uids.mastodon; | |
137 | group = cfg.group; | |
138 | description = "Mastodon user"; | |
139 | home = cfg.dataDir; | |
140 | useDefaultShell = true; | |
141 | }; | |
142 | }; | |
143 | users.groups = lib.optionalAttrs (cfg.group == name) { | |
144 | "${name}" = { | |
145 | gid = myuids.lib.gids.mastodon; | |
146 | }; | |
147 | }; | |
148 | ||
149 | systemd.slices.mastodon = { | |
150 | description = "Mastodon slice"; | |
151 | }; | |
152 | ||
153 | systemd.services.mastodon-streaming = { | |
154 | description = "Mastodon Streaming"; | |
155 | wantedBy = [ "multi-user.target" ]; | |
156 | after = [ "network.target" "mastodon-web.service" ]; | |
157 | ||
158 | environment.NODE_ENV = "production"; | |
159 | environment.SOCKET = cfg.sockets.node; | |
160 | ||
161 | path = [ cfg.workdir.nodejs pkgs.bashInteractive ]; | |
162 | ||
163 | script = '' | |
164 | exec npm run start | |
165 | ''; | |
166 | ||
167 | postStart = '' | |
168 | while [ ! -S $SOCKET ]; do | |
169 | sleep 0.5 | |
170 | done | |
171 | chmod a+w $SOCKET | |
172 | ''; | |
173 | ||
174 | postStop = '' | |
175 | rm $SOCKET | |
176 | ''; | |
177 | ||
178 | serviceConfig = { | |
179 | Slice = "mastodon.slice"; | |
180 | User = cfg.user; | |
181 | EnvironmentFile = cfg.configFile; | |
182 | PrivateTmp = true; | |
183 | Restart = "always"; | |
184 | TimeoutSec = 15; | |
185 | Type = "simple"; | |
186 | WorkingDirectory = cfg.workdir; | |
187 | StateDirectory = cfg.systemdStateDirectory; | |
188 | RuntimeDirectory = cfg.systemdRuntimeDirectory; | |
189 | RuntimeDirectoryPreserve = "yes"; | |
190 | }; | |
191 | ||
192 | unitConfig.RequiresMountsFor = cfg.dataDir; | |
193 | }; | |
194 | ||
195 | systemd.services.mastodon-web = { | |
196 | description = "Mastodon Web app"; | |
197 | wantedBy = [ "multi-user.target" ]; | |
198 | after = [ "network.target" ]; | |
199 | ||
200 | environment.RAILS_ENV = "production"; | |
201 | environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}"; | |
202 | environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile"; | |
203 | environment.SOCKET = cfg.sockets.rails; | |
204 | ||
205 | path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file pkgs.imagemagick ]; | |
206 | ||
207 | preStart = '' | |
208 | install -m 0755 -d ${cfg.dataDir}/tmp/cache | |
209 | ./bin/bundle exec rails db:migrate | |
210 | ''; | |
211 | ||
212 | script = '' | |
213 | exec ./bin/bundle exec puma -C config/puma.rb | |
214 | ''; | |
215 | ||
216 | postStart = '' | |
217 | exec ./bin/tootctl cache clear | |
218 | ''; | |
219 | serviceConfig = { | |
220 | Slice = "mastodon.slice"; | |
221 | User = cfg.user; | |
222 | EnvironmentFile = cfg.configFile; | |
223 | PrivateTmp = true; | |
224 | Restart = "always"; | |
225 | TimeoutSec = 60; | |
226 | Type = "simple"; | |
227 | WorkingDirectory = cfg.workdir; | |
228 | StateDirectory = cfg.systemdStateDirectory; | |
229 | RuntimeDirectory = cfg.systemdRuntimeDirectory; | |
230 | RuntimeDirectoryPreserve = "yes"; | |
231 | }; | |
232 | ||
233 | unitConfig.RequiresMountsFor = cfg.dataDir; | |
234 | }; | |
235 | ||
236 | # To be run manually because computationnally heavy | |
237 | systemd.services.mastodon-cleanup-manual = { | |
238 | description = "Cleanup mastodon"; | |
239 | ||
240 | environment.RAILS_ENV = "production"; | |
241 | environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}"; | |
242 | environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile"; | |
243 | environment.SOCKET = cfg.sockets.rails; | |
244 | ||
245 | path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file ]; | |
246 | ||
247 | script = '' | |
248 | exec ./bin/tootctl statuses remove --days 365 | |
249 | ''; | |
250 | ||
251 | serviceConfig = { | |
252 | User = cfg.user; | |
253 | EnvironmentFile = cfg.configFile; | |
254 | PrivateTmp = true; | |
255 | Type = "oneshot"; | |
256 | WorkingDirectory = cfg.workdir; | |
257 | StateDirectory = cfg.systemdStateDirectory; | |
258 | RuntimeDirectory = cfg.systemdRuntimeDirectory; | |
259 | RuntimeDirectoryPreserve = "yes"; | |
260 | }; | |
261 | ||
262 | unitConfig.RequiresMountsFor = cfg.dataDir; | |
263 | }; | |
264 | ||
265 | systemd.services.mastodon-cleanup = { | |
266 | description = "Cleanup mastodon"; | |
267 | startAt = "daily"; | |
268 | restartIfChanged = false; | |
269 | ||
270 | environment.RAILS_ENV = "production"; | |
271 | environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}"; | |
272 | environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile"; | |
273 | environment.SOCKET = cfg.sockets.rails; | |
274 | ||
275 | path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file ]; | |
276 | ||
277 | script = '' | |
278 | exec ./bin/tootctl media remove --days 30 | |
279 | ''; | |
280 | ||
281 | serviceConfig = { | |
282 | User = cfg.user; | |
283 | EnvironmentFile = cfg.configFile; | |
284 | PrivateTmp = true; | |
285 | Type = "oneshot"; | |
286 | WorkingDirectory = cfg.workdir; | |
287 | StateDirectory = cfg.systemdStateDirectory; | |
288 | RuntimeDirectory = cfg.systemdRuntimeDirectory; | |
289 | RuntimeDirectoryPreserve = "yes"; | |
290 | }; | |
291 | ||
292 | unitConfig.RequiresMountsFor = cfg.dataDir; | |
293 | }; | |
294 | ||
295 | systemd.services.mastodon-sidekiq = { | |
296 | description = "Mastodon Sidekiq"; | |
297 | wantedBy = [ "multi-user.target" ]; | |
298 | after = [ "network.target" "mastodon-web.service" ]; | |
299 | ||
300 | environment.RAILS_ENV="production"; | |
301 | environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}"; | |
302 | environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile"; | |
303 | environment.DB_POOL="5"; | |
304 | ||
305 | path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.imagemagick pkgs.ffmpeg pkgs.file ]; | |
306 | ||
307 | script = '' | |
308 | exec ./bin/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push | |
309 | ''; | |
310 | ||
311 | serviceConfig = { | |
312 | Slice = "mastodon.slice"; | |
313 | User = cfg.user; | |
314 | EnvironmentFile = cfg.configFile; | |
315 | PrivateTmp = true; | |
316 | Restart = "always"; | |
317 | TimeoutSec = 15; | |
318 | Type = "simple"; | |
319 | WorkingDirectory = cfg.workdir; | |
320 | StateDirectory = cfg.systemdStateDirectory; | |
321 | RuntimeDirectory = cfg.systemdRuntimeDirectory; | |
322 | RuntimeDirectoryPreserve = "yes"; | |
323 | }; | |
324 | ||
325 | unitConfig.RequiresMountsFor = cfg.dataDir; | |
326 | }; | |
327 | ||
328 | }; | |
329 | }; | |
330 | }; | |
331 | } | |
332 | ||
333 |