9 cfg = config.services.misskey;
10 settingsFormat = pkgs.formats.yaml { };
11 redisType = lib.types.submodule {
12 freeformType = lib.types.attrsOf settingsFormat.type;
16 default = "localhost";
17 description = "The Redis host.";
20 type = lib.types.port;
22 description = "The Redis port.";
26 settings = lib.mkOption {
28 Configuration for Misskey, see
29 [`example.yml`](https://github.com/misskey-dev/misskey/blob/develop/.config/example.yml)
30 for all supported options.
32 type = lib.types.submodule {
33 freeformType = lib.types.attrsOf settingsFormat.type;
37 example = "https://example.tld/";
39 The final user-facing URL. Do not change after running Misskey for the first time.
41 This needs to match up with the configured reverse proxy and is automatically configured when using `services.misskey.reverseProxy`.
45 type = lib.types.port;
47 description = "The port your Misskey server should listen on.";
49 socket = lib.mkOption {
50 type = lib.types.nullOr lib.types.path;
52 example = "/path/to/misskey.sock";
53 description = "The UNIX socket your Misskey server should listen on.";
55 chmodSocket = lib.mkOption {
56 type = lib.types.nullOr lib.types.str;
59 description = "The file access mode of the UNIX socket.";
62 description = "Database settings.";
63 type = lib.types.submodule {
67 default = "/var/run/postgresql";
68 example = "localhost";
69 description = "The PostgreSQL host.";
72 type = lib.types.port;
74 description = "The PostgreSQL port.";
79 description = "The database name.";
84 description = "The user used for database authentication.";
87 type = lib.types.nullOr lib.types.str;
89 description = "The password used for database authentication.";
91 disableCache = lib.mkOption {
92 type = lib.types.bool;
94 description = "Whether to disable caching queries.";
96 extra = lib.mkOption {
97 type = lib.types.nullOr (lib.types.attrsOf settingsFormat.type);
102 description = "Extra connection options.";
108 redis = lib.mkOption {
111 description = "`ioredis` options. See [`README`](https://github.com/redis/ioredis?tab=readme-ov-file#connect-to-redis) for reference.";
113 redisForPubsub = lib.mkOption {
114 type = lib.types.nullOr redisType;
116 description = "`ioredis` options for pubsub. See [`README`](https://github.com/redis/ioredis?tab=readme-ov-file#connect-to-redis) for reference.";
118 redisForJobQueue = lib.mkOption {
119 type = lib.types.nullOr redisType;
121 description = "`ioredis` options for the job queue. See [`README`](https://github.com/redis/ioredis?tab=readme-ov-file#connect-to-redis) for reference.";
123 redisForTimelines = lib.mkOption {
124 type = lib.types.nullOr redisType;
126 description = "`ioredis` options for timelines. See [`README`](https://github.com/redis/ioredis?tab=readme-ov-file#connect-to-redis) for reference.";
128 meilisearch = lib.mkOption {
129 description = "Meilisearch connection options.";
130 type = lib.types.nullOr (
131 lib.types.submodule {
133 host = lib.mkOption {
134 type = lib.types.str;
135 default = "localhost";
136 description = "The Meilisearch host.";
138 port = lib.mkOption {
139 type = lib.types.port;
141 description = "The Meilisearch port.";
143 apiKey = lib.mkOption {
144 type = lib.types.nullOr lib.types.str;
146 description = "The Meilisearch API key.";
149 type = lib.types.bool;
151 description = "Whether to connect via SSL.";
153 index = lib.mkOption {
154 type = lib.types.nullOr lib.types.str;
156 description = "Meilisearch index to use.";
158 scope = lib.mkOption {
159 type = lib.types.enum [
164 description = "The search scope.";
172 type = lib.types.enum [
180 description = "The ID generation method to use. Do not change after starting Misskey for the first time.";
190 enable = lib.mkEnableOption "misskey";
191 package = lib.mkPackageOption pkgs "misskey" { };
194 createLocally = lib.mkOption {
195 type = lib.types.bool;
197 description = "Create the PostgreSQL database locally. Sets `services.misskey.settings.db.{db,host,port,user,pass}`.";
199 passwordFile = lib.mkOption {
200 type = lib.types.nullOr lib.types.path;
202 description = "The path to a file containing the database password. Sets `services.misskey.settings.db.pass`.";
206 createLocally = lib.mkOption {
207 type = lib.types.bool;
209 description = "Create and use a local Redis instance. Sets `services.misskey.settings.redis.host`.";
211 passwordFile = lib.mkOption {
212 type = lib.types.nullOr lib.types.path;
214 description = "The path to a file containing the Redis password. Sets `services.misskey.settings.redis.pass`.";
218 createLocally = lib.mkOption {
219 type = lib.types.bool;
221 description = "Create and use a local Meilisearch instance. Sets `services.misskey.settings.meilisearch.{host,port,ssl}`.";
223 keyFile = lib.mkOption {
224 type = lib.types.nullOr lib.types.path;
226 description = "The path to a file containing the Meilisearch API key. Sets `services.misskey.settings.meilisearch.apiKey`.";
230 enable = lib.mkEnableOption "a HTTP reverse proxy for Misskey";
231 webserver = lib.mkOption {
232 type = lib.types.attrTag {
233 nginx = lib.mkOption {
234 type = lib.types.submodule (import ../web-servers/nginx/vhost-options.nix);
237 Extra configuration for the nginx virtual host of Misskey.
238 Set to `{ }` to use the default configuration.
241 caddy = lib.mkOption {
242 type = lib.types.submodule (
243 import ../web-servers/caddy/vhost-options.nix { cfg = config.services.caddy; }
247 Extra configuration for the caddy virtual host of Misskey.
248 Set to `{ }` to use the default configuration.
252 description = "The webserver to use as the reverse proxy.";
254 host = lib.mkOption {
255 type = lib.types.nullOr lib.types.str;
257 The fully qualified domain name to bind to. Sets `services.misskey.settings.url`.
259 This is required when using `services.misskey.reverseProxy.enable = true`.
261 example = "misskey.example.com";
265 type = lib.types.nullOr lib.types.bool;
267 Whether to enable SSL for the reverse proxy. Sets `services.misskey.settings.url`.
269 This is required when using `services.misskey.reverseProxy.enable = true`.
278 config = lib.mkIf cfg.enable {
282 cfg.reverseProxy.enable -> ((cfg.reverseProxy.host != null) && (cfg.reverseProxy.ssl != null));
283 message = "`services.misskey.reverseProxy.enable` requires `services.misskey.reverseProxy.host` and `services.misskey.reverseProxy.ssl` to be set.";
287 services.misskey.settings = lib.mkMerge [
288 (lib.mkIf cfg.database.createLocally {
290 db = lib.mkDefault "misskey";
291 # Use unix socket instead of localhost to allow PostgreSQL peer authentication,
292 # required for `services.postgresql.ensureUsers`
293 host = lib.mkDefault "/var/run/postgresql";
294 port = lib.mkDefault config.services.postgresql.settings.port;
295 user = lib.mkDefault "misskey";
296 pass = lib.mkDefault null;
299 (lib.mkIf (cfg.database.passwordFile != null) { db.pass = lib.mkDefault "@DATABASE_PASSWORD@"; })
300 (lib.mkIf cfg.redis.createLocally { redis.host = lib.mkDefault "localhost"; })
301 (lib.mkIf (cfg.redis.passwordFile != null) { redis.pass = lib.mkDefault "@REDIS_PASSWORD@"; })
302 (lib.mkIf cfg.meilisearch.createLocally {
304 host = lib.mkDefault "localhost";
305 port = lib.mkDefault config.services.meilisearch.listenPort;
306 ssl = lib.mkDefault false;
309 (lib.mkIf (cfg.meilisearch.keyFile != null) {
310 meilisearch.apiKey = lib.mkDefault "@MEILISEARCH_KEY@";
312 (lib.mkIf cfg.reverseProxy.enable {
313 url = lib.mkDefault "${
314 if cfg.reverseProxy.ssl then "https" else "http"
315 }://${cfg.reverseProxy.host}";
319 systemd.services.misskey = {
321 "network-online.target"
324 wants = [ "network-online.target" ];
325 wantedBy = [ "multi-user.target" ];
327 MISSKEY_CONFIG_YML = "/run/misskey/default.yml";
331 install -m 700 ${settingsFormat.generate "misskey-config.yml" cfg.settings} /run/misskey/default.yml
333 + (lib.optionalString (cfg.database.passwordFile != null) ''
334 ${pkgs.replace-secret}/bin/replace-secret '@DATABASE_PASSWORD@' "${cfg.database.passwordFile}" /run/misskey/default.yml
336 + (lib.optionalString (cfg.redis.passwordFile != null) ''
337 ${pkgs.replace-secret}/bin/replace-secret '@REDIS_PASSWORD@' "${cfg.redis.passwordFile}" /run/misskey/default.yml
339 + (lib.optionalString (cfg.meilisearch.keyFile != null) ''
340 ${pkgs.replace-secret}/bin/replace-secret '@MEILISEARCH_KEY@' "${cfg.meilisearch.keyFile}" /run/misskey/default.yml
343 ExecStart = "${cfg.package}/bin/misskey migrateandstart";
344 RuntimeDirectory = "misskey";
345 RuntimeDirectoryMode = "700";
346 StateDirectory = "misskey";
347 StateDirectoryMode = "700";
351 LockPersonality = true;
352 PrivateDevices = true;
355 ProtectControlGroups = true;
357 ProtectHostname = true;
358 ProtectKernelLogs = true;
359 ProtectProc = "invisible";
360 ProtectKernelModules = true;
361 ProtectKernelTunables = true;
362 RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX AF_NETLINK";
366 services.postgresql = lib.mkIf cfg.database.createLocally {
368 ensureDatabases = [ "misskey" ];
372 ensureDBOwnership = true;
377 services.redis.servers = lib.mkIf cfg.redis.createLocally {
380 port = cfg.settings.redis.port;
384 services.meilisearch = lib.mkIf cfg.meilisearch.createLocally { enable = true; };
386 services.caddy = lib.mkIf (cfg.reverseProxy.enable && cfg.reverseProxy.webserver ? caddy) {
388 virtualHosts.${cfg.settings.url} = lib.mkMerge [
389 cfg.reverseProxy.webserver.caddy
391 hostName = lib.mkDefault cfg.settings.url;
393 reverse_proxy localhost:${toString cfg.settings.port}
399 services.nginx = lib.mkIf (cfg.reverseProxy.enable && cfg.reverseProxy.webserver ? nginx) {
401 virtualHosts.${cfg.reverseProxy.host} = lib.mkMerge [
402 cfg.reverseProxy.webserver.nginx
405 proxyPass = lib.mkDefault "http://localhost:${toString cfg.settings.port}";
406 proxyWebsockets = lib.mkDefault true;
407 recommendedProxySettings = lib.mkDefault true;
410 (lib.mkIf (cfg.reverseProxy.ssl != null) { forceSSL = lib.mkDefault cfg.reverseProxy.ssl; })
416 maintainers = [ lib.maintainers.feathecutie ];