2 description = "Your self-hosted, globally interconnected microblogging community";
4 url = "path:../myuids";
6 inputs.flake-utils.url = "github:numtide/flake-utils";
8 url = "github:NixOS/nixpkgs/840c782d507d60aaa49aa9e3f6d0b0e780912742";
12 url = "github:tootsuite/mastodon/v2.9.4";
16 outputs = { self, myuids, nixpkgs, mastodon, flake-utils }: flake-utils.lib.eachSystem ["x86_64-linux"] (system:
18 pkgs = import nixpkgs { inherit system; overlays = []; };
19 version = (builtins.fromJSON (builtins.readFile ./flake.lock)).nodes.mastodon.original.ref;
20 inherit (pkgs) callPackage;
22 packages.mastodon = callPackage ./. { src = mastodon // { inherit version; }; };
23 defaultPackage = packages.mastodon;
24 legacyPackages.mastodon = packages.mastodon;
26 build = defaultPackage;
31 mastodon = final: prev: {
32 mastodon = self.defaultPackage."${final.system}";
35 overlay = overlays.mastodon;
36 nixosModule = { lib, pkgs, config, ... }:
39 cfg = config.immaeServices.mastodon;
42 options.immaeServices.mastodon = {
43 enable = lib.mkEnableOption "Enable Mastodon’s service";
47 description = "User account under which Mastodon runs";
49 group = lib.mkOption {
52 description = "Group under which Mastodon runs";
54 dataDir = lib.mkOption {
55 type = lib.types.path;
56 default = "/var/lib/${name}";
58 The directory where Mastodon stores its data.
61 socketsPrefix = lib.mkOption {
65 The prefix to use for Mastodon sockets.
68 socketsDir = lib.mkOption {
69 type = lib.types.path;
70 default = "/run/${name}";
72 The directory where Mastodon puts runtime files and sockets.
75 configFile = lib.mkOption {
76 type = lib.types.path;
78 The configuration file path for Mastodon.
81 package = lib.mkOption {
82 type = lib.types.package;
83 default = pkgs.mastodon;
85 Mastodon package to use.
89 workdir = lib.mkOption {
90 type = lib.types.package;
91 default = cfg.package.override { varDir = cfg.dataDir; };
93 Adjusted mastodon package with overriden varDir
97 systemdStateDirectory = lib.mkOption {
99 # Use ReadWritePaths= instead if varDir is outside of /var/lib
100 default = assert lib.strings.hasPrefix "/var/lib/" cfg.dataDir;
101 lib.strings.removePrefix "/var/lib/" cfg.dataDir;
103 Adjusted Mastodon data directory for systemd
107 systemdRuntimeDirectory = lib.mkOption {
108 type = lib.types.str;
109 # Use ReadWritePaths= instead if socketsDir is outside of /run
110 default = assert lib.strings.hasPrefix "/run/" cfg.socketsDir;
111 lib.strings.removePrefix "/run/" cfg.socketsDir;
113 Adjusted Mastodon sockets directory for systemd
117 sockets = lib.mkOption {
118 type = lib.types.attrsOf lib.types.path;
120 node = "${cfg.socketsDir}/${cfg.socketsPrefix}_node.sock";
121 rails = "${cfg.socketsDir}/${cfg.socketsPrefix}_puma.sock";
130 config = lib.mkIf cfg.enable {
131 nixpkgs.overlays = [ self.overlay ];
132 users.users = lib.optionalAttrs (cfg.user == name) {
134 uid = myuids.lib.uids.mastodon;
136 description = "Mastodon user";
138 useDefaultShell = true;
141 users.groups = lib.optionalAttrs (cfg.group == name) {
143 gid = myuids.lib.gids.mastodon;
147 systemd.slices.mastodon = {
148 description = "Mastodon slice";
151 systemd.services.mastodon-streaming = {
152 description = "Mastodon Streaming";
153 wantedBy = [ "multi-user.target" ];
154 after = [ "network.target" "mastodon-web.service" ];
156 environment.NODE_ENV = "production";
157 environment.SOCKET = cfg.sockets.node;
159 path = [ cfg.workdir.nodejs pkgs.bashInteractive ];
166 while [ ! -S $SOCKET ]; do
177 Slice = "mastodon.slice";
179 EnvironmentFile = cfg.configFile;
184 WorkingDirectory = cfg.workdir;
185 StateDirectory = cfg.systemdStateDirectory;
186 RuntimeDirectory = cfg.systemdRuntimeDirectory;
187 RuntimeDirectoryPreserve = "yes";
190 unitConfig.RequiresMountsFor = cfg.dataDir;
193 systemd.services.mastodon-web = {
194 description = "Mastodon Web app";
195 wantedBy = [ "multi-user.target" ];
196 after = [ "network.target" ];
198 environment.RAILS_ENV = "production";
199 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
200 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
201 environment.SOCKET = cfg.sockets.rails;
203 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file pkgs.imagemagick ];
206 install -m 0755 -d ${cfg.dataDir}/tmp/cache
207 ./bin/bundle exec rails db:migrate
211 exec ./bin/bundle exec puma -C config/puma.rb
215 exec ./bin/tootctl cache clear
218 Slice = "mastodon.slice";
220 EnvironmentFile = cfg.configFile;
225 WorkingDirectory = cfg.workdir;
226 StateDirectory = cfg.systemdStateDirectory;
227 RuntimeDirectory = cfg.systemdRuntimeDirectory;
228 RuntimeDirectoryPreserve = "yes";
231 unitConfig.RequiresMountsFor = cfg.dataDir;
234 # To be run manually because computationnally heavy
235 systemd.services.mastodon-cleanup-manual = {
236 description = "Cleanup mastodon";
238 environment.RAILS_ENV = "production";
239 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
240 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
241 environment.SOCKET = cfg.sockets.rails;
243 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file ];
246 exec ./bin/tootctl statuses remove --days 365
251 EnvironmentFile = cfg.configFile;
254 WorkingDirectory = cfg.workdir;
255 StateDirectory = cfg.systemdStateDirectory;
256 RuntimeDirectory = cfg.systemdRuntimeDirectory;
257 RuntimeDirectoryPreserve = "yes";
260 unitConfig.RequiresMountsFor = cfg.dataDir;
263 systemd.services.mastodon-cleanup = {
264 description = "Cleanup mastodon";
266 restartIfChanged = false;
268 environment.RAILS_ENV = "production";
269 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
270 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
271 environment.SOCKET = cfg.sockets.rails;
273 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.file ];
276 exec ./bin/tootctl media remove --days 30
281 EnvironmentFile = cfg.configFile;
284 WorkingDirectory = cfg.workdir;
285 StateDirectory = cfg.systemdStateDirectory;
286 RuntimeDirectory = cfg.systemdRuntimeDirectory;
287 RuntimeDirectoryPreserve = "yes";
290 unitConfig.RequiresMountsFor = cfg.dataDir;
293 systemd.services.mastodon-sidekiq = {
294 description = "Mastodon Sidekiq";
295 wantedBy = [ "multi-user.target" ];
296 after = [ "network.target" "mastodon-web.service" ];
298 environment.RAILS_ENV="production";
299 environment.BUNDLE_PATH = "${cfg.workdir.gems}/${cfg.workdir.gems.ruby.gemPath}";
300 environment.BUNDLE_GEMFILE = "${cfg.workdir.gems.confFiles}/Gemfile";
301 environment.DB_POOL="5";
303 path = [ cfg.workdir.gems cfg.workdir.gems.ruby pkgs.imagemagick pkgs.ffmpeg pkgs.file ];
306 exec ./bin/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
310 Slice = "mastodon.slice";
312 EnvironmentFile = cfg.configFile;
317 WorkingDirectory = cfg.workdir;
318 StateDirectory = cfg.systemdStateDirectory;
319 RuntimeDirectory = cfg.systemdRuntimeDirectory;
320 RuntimeDirectoryPreserve = "yes";
323 unitConfig.RequiresMountsFor = cfg.dataDir;