git.iko.soy/typeable/nixpkgs
on master help sign in
nixos/lemmy: init

Co-authored-by: Raphael Megzari <raphael@megzari.com>

commitcc3b147ed182a6cae239348ef094158815da14ae authorBill Ewanick <bill@ewanick.com> dateThu 23 Sep 2021, 10:27 (5 years ago) parent8a896d6
5 files changed+331−1
nixos/modules/module-list.nix+1 nixos/modules/services/web-apps/lemmy.md+34 nixos/modules/services/web-apps/lemmy.nix+238 nixos/modules/services/web-apps/lemmy.xml+56 pkgs/servers/web-apps/lemmy/ui.nix+2−1
nixos/modules/module-list.nix+1
@@ -985,6 +985,7 @@
985985 ./services/web-apps/jirafeau.nix
986986 ./services/web-apps/jitsi-meet.nix
987987 ./services/web-apps/keycloak.nix
988+ ./services/web-apps/lemmy.nix
988989 ./services/web-apps/limesurvey.nix
989990 ./services/web-apps/mastodon.nix
990991 ./services/web-apps/mattermost.nix
nixos/modules/services/web-apps/lemmy.mdnew file+34
@@ -0,0 +1,34 @@
1+# Lemmy {#module-services-lemmy}
2+
3+Lemmy is a federated alternative to reddit in rust.
4+
5+## Quickstart {#module-services-lemmy-quickstart}
6+
7+the minimum to start lemmy is
8+
9+```nix
10+services.lemmy = {
11+ enable = true;
12+ settings = {
13+ hostname = "lemmy.union.rocks";
14+ database.createLocally = true;
15+ };
16+ jwtSecretPath = "/run/secrets/lemmyJwt";
17+ caddy.enable = true;
18+}
19+```
20+
21+(note that you can use something like agenix to get your secret jwt to the specified path)
22+
23+this will start the backend on port 8536 and the frontend on port 1234.
24+It will expose your instance with a caddy reverse proxy to the hostname you've provided.
25+Postgres will be initialized on that same instance automatically.
26+
27+## Usage {#module-services-lemmy-usage}
28+
29+On first connection you will be asked to define an admin user.
30+
31+## Missing {#module-services-lemmy-missing}
32+
33+- Exposing with nginx is not implemented yet.
34+- This has been tested using a local database with a unix socket connection. Using different database settings will likely require modifications
nixos/modules/services/web-apps/lemmy.nixnew file+238
@@ -0,0 +1,238 @@
1+{ lib, pkgs, config, ... }:
2+with lib;
3+let
4+ cfg = config.services.lemmy;
5+ settingsFormat = pkgs.formats.json { };
6+in
7+{
8+ meta.maintainers = with maintainers; [ happysalada ];
9+ # Don't edit the docbook xml directly, edit the md and generate it:
10+ # `pandoc lemmy.md -t docbook --top-level-division=chapter --extract-media=media -f markdown+smart > lemmy.xml`
11+ meta.doc = ./lemmy.xml;
12+
13+ options.services.lemmy = {
14+
15+ enable = mkEnableOption "lemmy a federated alternative to reddit in rust";
16+
17+ jwtSecretPath = mkOption {
18+ type = types.path;
19+ description = "Path to read the jwt secret from.";
20+ };
21+
22+ ui = {
23+ port = mkOption {
24+ type = types.port;
25+ default = 1234;
26+ description = "Port where lemmy-ui should listen for incoming requests.";
27+ };
28+ };
29+
30+ caddy.enable = mkEnableOption "exposing lemmy with the caddy reverse proxy";
31+
32+ settings = mkOption {
33+ default = { };
34+ description = "Lemmy configuration";
35+
36+ type = types.submodule {
37+ freeformType = settingsFormat.type;
38+
39+ options.hostname = mkOption {
40+ type = types.str;
41+ default = null;
42+ description = "The domain name of your instance (eg 'lemmy.ml').";
43+ };
44+
45+ options.port = mkOption {
46+ type = types.port;
47+ default = 8536;
48+ description = "Port where lemmy should listen for incoming requests.";
49+ };
50+
51+ options.federation = {
52+ enabled = mkEnableOption "activitypub federation";
53+ };
54+
55+ options.captcha = {
56+ enabled = mkOption {
57+ type = types.bool;
58+ default = true;
59+ description = "Enable Captcha.";
60+ };
61+ difficulty = mkOption {
62+ type = types.enum [ "easy" "medium" "hard" ];
63+ default = "medium";
64+ description = "The difficultly of the captcha to solve.";
65+ };
66+ };
67+
68+ options.database.createLocally = mkEnableOption "creation of database on the instance";
69+
70+ };
71+ };
72+
73+ };
74+
75+ config =
76+ let
77+ localPostgres = (cfg.settings.database.host == "localhost" || cfg.settings.database.host == "/run/postgresql");
78+ in
79+ lib.mkIf cfg.enable {
80+ services.lemmy.settings = (mapAttrs (name: mkDefault)
81+ {
82+ bind = "127.0.0.1";
83+ tls_enabled = true;
84+ pictrs_url = with config.services.pict-rs; "http://${address}:${toString port}";
85+ actor_name_max_length = 20;
86+
87+ rate_limit.message = 180;
88+ rate_limit.message_per_second = 60;
89+ rate_limit.post = 6;
90+ rate_limit.post_per_second = 600;
91+ rate_limit.register = 3;
92+ rate_limit.register_per_second = 3600;
93+ rate_limit.image = 6;
94+ rate_limit.image_per_second = 3600;
95+ } // {
96+ database = mapAttrs (name: mkDefault) {
97+ user = "lemmy";
98+ host = "/run/postgresql";
99+ port = 5432;
100+ database = "lemmy";
101+ pool_size = 5;
102+ };
103+ });
104+
105+ services.postgresql = mkIf localPostgres {
106+ enable = mkDefault true;
107+ };
108+
109+ services.pict-rs.enable = true;
110+
111+ services.caddy = mkIf cfg.caddy.enable {
112+ enable = mkDefault true;
113+ virtualHosts."${cfg.settings.hostname}" = {
114+ extraConfig = ''
115+ handle_path /static/* {
116+ root * ${pkgs.lemmy-ui}/dist
117+ file_server
118+ }
119+ @for_backend {
120+ path /api/* /pictrs/* feeds/* nodeinfo/*
121+ }
122+ handle @for_backend {
123+ reverse_proxy 127.0.0.1:${toString cfg.settings.port}
124+ }
125+ @post {
126+ method POST
127+ }
128+ handle @post {
129+ reverse_proxy 127.0.0.1:${toString cfg.settings.port}
130+ }
131+ @jsonld {
132+ header Accept "application/activity+json"
133+ header Accept "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
134+ }
135+ handle @jsonld {
136+ reverse_proxy 127.0.0.1:${toString cfg.settings.port}
137+ }
138+ handle {
139+ reverse_proxy 127.0.0.1:${toString cfg.ui.port}
140+ }
141+ '';
142+ };
143+ };
144+
145+ assertions = [{
146+ assertion = cfg.settings.database.createLocally -> localPostgres;
147+ message = "if you want to create the database locally, you need to use a local database";
148+ }];
149+
150+ systemd.services.lemmy = {
151+ description = "Lemmy server";
152+
153+ environment = {
154+ LEMMY_CONFIG_LOCATION = "/run/lemmy/config.hjson";
155+
156+ # Verify how this is used, and don't put the password in the nix store
157+ LEMMY_DATABASE_URL = with cfg.settings.database;"postgres:///${database}?host=${host}";
158+ };
159+
160+ documentation = [
161+ "https://join-lemmy.org/docs/en/administration/from_scratch.html"
162+ "https://join-lemmy.org/docs"
163+ ];
164+
165+ wantedBy = [ "multi-user.target" ];
166+
167+ after = [ "pict-rs.service " ] ++ lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ];
168+
169+ requires = lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ];
170+
171+ # script is needed here since loadcredential is not accessible on ExecPreStart
172+ script = ''
173+ ${pkgs.coreutils}/bin/install -m 600 ${settingsFormat.generate "config.hjson" cfg.settings} /run/lemmy/config.hjson
174+ jwtSecret="$(< $CREDENTIALS_DIRECTORY/jwt_secret )"
175+ ${pkgs.jq}/bin/jq ".jwt_secret = \"$jwtSecret\"" /run/lemmy/config.hjson | ${pkgs.moreutils}/bin/sponge /run/lemmy/config.hjson
176+ ${pkgs.lemmy-server}/bin/lemmy_server
177+ '';
178+
179+ serviceConfig = {
180+ DynamicUser = true;
181+ RuntimeDirectory = "lemmy";
182+ LoadCredential = "jwt_secret:${cfg.jwtSecretPath}";
183+ };
184+ };
185+
186+ systemd.services.lemmy-ui = {
187+ description = "Lemmy ui";
188+
189+ environment = {
190+ LEMMY_UI_HOST = "127.0.0.1:${toString cfg.ui.port}";
191+ LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}";
192+ LEMMY_EXTERNAL_HOST = cfg.settings.hostname;
193+ LEMMY_HTTPS = "false";
194+ };
195+
196+ documentation = [
197+ "https://join-lemmy.org/docs/en/administration/from_scratch.html"
198+ "https://join-lemmy.org/docs"
199+ ];
200+
201+ wantedBy = [ "multi-user.target" ];
202+
203+ after = [ "lemmy.service" ];
204+
205+ requires = [ "lemmy.service" ];
206+
207+ serviceConfig = {
208+ DynamicUser = true;
209+ WorkingDirectory = "${pkgs.lemmy-ui}";
210+ ExecStart = "${pkgs.nodejs}/bin/node ${pkgs.lemmy-ui}/dist/js/server.js";
211+ };
212+ };
213+
214+ systemd.services.lemmy-postgresql = mkIf cfg.settings.database.createLocally {
215+ description = "Lemmy postgresql db";
216+ after = [ "postgresql.service" ];
217+ bindsTo = [ "postgresql.service" ];
218+ requiredBy = [ "lemmy.service" ];
219+ partOf = [ "lemmy.service" ];
220+ script = with cfg.settings.database; ''
221+ PSQL() {
222+ ${config.services.postgresql.package}/bin/psql --port=${toString cfg.settings.database.port} "$@"
223+ }
224+ # check if the database already exists
225+ if ! PSQL -lqt | ${pkgs.coreutils}/bin/cut -d \| -f 1 | ${pkgs.gnugrep}/bin/grep -qw ${database} ; then
226+ PSQL -tAc "CREATE ROLE ${user} WITH LOGIN;"
227+ PSQL -tAc "CREATE DATABASE ${database} WITH OWNER ${user};"
228+ fi
229+ '';
230+ serviceConfig = {
231+ User = config.services.postgresql.superUser;
232+ Type = "oneshot";
233+ RemainAfterExit = true;
234+ };
235+ };
236+ };
237+
238+}
nixos/modules/services/web-apps/lemmy.xmlnew file+56
@@ -0,0 +1,56 @@
1+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-lemmy">
2+ <title>Lemmy</title>
3+ <para>
4+ Lemmy is a federated alternative to reddit in rust.
5+ </para>
6+ <section xml:id="module-services-lemmy-quickstart">
7+ <title>Quickstart</title>
8+ <para>
9+ the minimum to start lemmy is
10+ </para>
11+ <programlisting language="bash">
12+services.lemmy = {
13+ enable = true;
14+ settings = {
15+ hostname = &quot;lemmy.union.rocks&quot;;
16+ database.createLocally = true;
17+ };
18+ jwtSecretPath = &quot;/run/secrets/lemmyJwt&quot;;
19+ caddy.enable = true;
20+}
21+</programlisting>
22+ <para>
23+ (note that you can use something like agenix to get your secret
24+ jwt to the specified path)
25+ </para>
26+ <para>
27+ this will start the backend on port 8536 and the frontend on port
28+ 1234. It will expose your instance with a caddy reverse proxy to
29+ the hostname you’ve provided. Postgres will be initialized on that
30+ same instance automatically.
31+ </para>
32+ </section>
33+ <section xml:id="module-services-lemmy-usage">
34+ <title>Usage</title>
35+ <para>
36+ On first connection you will be asked to define an admin user.
37+ </para>
38+ </section>
39+ <section xml:id="module-services-lemmy-missing">
40+ <title>Missing</title>
41+ <itemizedlist spacing="compact">
42+ <listitem>
43+ <para>
44+ Exposing with nginx is not implemented yet.
45+ </para>
46+ </listitem>
47+ <listitem>
48+ <para>
49+ This has been tested using a local database with a unix socket
50+ connection. Using different database settings will likely
51+ require modifications
52+ </para>
53+ </listitem>
54+ </itemizedlist>
55+ </section>
56+</chapter>
pkgs/servers/web-apps/lemmy/ui.nix+2−1
@@ -57,7 +57,8 @@ mkYarnPackage {
5757
5858 preInstall = ''
5959 mkdir $out
60− cp -R ./deps/lemmy-ui/dist/assets $out
60+ cp -R ./deps/lemmy-ui/dist $out
61+ cp -R ./node_modules $out
6162 '';
6263
6364 distPhase = "true";