parent
8a896d686d
commit
cc3b147ed1
@ -0,0 +1,34 @@ |
||||
# Lemmy {#module-services-lemmy} |
||||
|
||||
Lemmy is a federated alternative to reddit in rust. |
||||
|
||||
## Quickstart {#module-services-lemmy-quickstart} |
||||
|
||||
the minimum to start lemmy is |
||||
|
||||
```nix |
||||
services.lemmy = { |
||||
enable = true; |
||||
settings = { |
||||
hostname = "lemmy.union.rocks"; |
||||
database.createLocally = true; |
||||
}; |
||||
jwtSecretPath = "/run/secrets/lemmyJwt"; |
||||
caddy.enable = true; |
||||
} |
||||
``` |
||||
|
||||
(note that you can use something like agenix to get your secret jwt to the specified path) |
||||
|
||||
this will start the backend on port 8536 and the frontend on port 1234. |
||||
It will expose your instance with a caddy reverse proxy to the hostname you've provided. |
||||
Postgres will be initialized on that same instance automatically. |
||||
|
||||
## Usage {#module-services-lemmy-usage} |
||||
|
||||
On first connection you will be asked to define an admin user. |
||||
|
||||
## Missing {#module-services-lemmy-missing} |
||||
|
||||
- Exposing with nginx is not implemented yet. |
||||
- This has been tested using a local database with a unix socket connection. Using different database settings will likely require modifications |
@ -0,0 +1,238 @@ |
||||
{ lib, pkgs, config, ... }: |
||||
with lib; |
||||
let |
||||
cfg = config.services.lemmy; |
||||
settingsFormat = pkgs.formats.json { }; |
||||
in |
||||
{ |
||||
meta.maintainers = with maintainers; [ happysalada ]; |
||||
# Don't edit the docbook xml directly, edit the md and generate it: |
||||
# `pandoc lemmy.md -t docbook --top-level-division=chapter --extract-media=media -f markdown+smart > lemmy.xml` |
||||
meta.doc = ./lemmy.xml; |
||||
|
||||
options.services.lemmy = { |
||||
|
||||
enable = mkEnableOption "lemmy a federated alternative to reddit in rust"; |
||||
|
||||
jwtSecretPath = mkOption { |
||||
type = types.path; |
||||
description = "Path to read the jwt secret from."; |
||||
}; |
||||
|
||||
ui = { |
||||
port = mkOption { |
||||
type = types.port; |
||||
default = 1234; |
||||
description = "Port where lemmy-ui should listen for incoming requests."; |
||||
}; |
||||
}; |
||||
|
||||
caddy.enable = mkEnableOption "exposing lemmy with the caddy reverse proxy"; |
||||
|
||||
settings = mkOption { |
||||
default = { }; |
||||
description = "Lemmy configuration"; |
||||
|
||||
type = types.submodule { |
||||
freeformType = settingsFormat.type; |
||||
|
||||
options.hostname = mkOption { |
||||
type = types.str; |
||||
default = null; |
||||
description = "The domain name of your instance (eg 'lemmy.ml')."; |
||||
}; |
||||
|
||||
options.port = mkOption { |
||||
type = types.port; |
||||
default = 8536; |
||||
description = "Port where lemmy should listen for incoming requests."; |
||||
}; |
||||
|
||||
options.federation = { |
||||
enabled = mkEnableOption "activitypub federation"; |
||||
}; |
||||
|
||||
options.captcha = { |
||||
enabled = mkOption { |
||||
type = types.bool; |
||||
default = true; |
||||
description = "Enable Captcha."; |
||||
}; |
||||
difficulty = mkOption { |
||||
type = types.enum [ "easy" "medium" "hard" ]; |
||||
default = "medium"; |
||||
description = "The difficultly of the captcha to solve."; |
||||
}; |
||||
}; |
||||
|
||||
options.database.createLocally = mkEnableOption "creation of database on the instance"; |
||||
|
||||
}; |
||||
}; |
||||
|
||||
}; |
||||
|
||||
config = |
||||
let |
||||
localPostgres = (cfg.settings.database.host == "localhost" || cfg.settings.database.host == "/run/postgresql"); |
||||
in |
||||
lib.mkIf cfg.enable { |
||||
services.lemmy.settings = (mapAttrs (name: mkDefault) |
||||
{ |
||||
bind = "127.0.0.1"; |
||||
tls_enabled = true; |
||||
pictrs_url = with config.services.pict-rs; "http://${address}:${toString port}"; |
||||
actor_name_max_length = 20; |
||||
|
||||
rate_limit.message = 180; |
||||
rate_limit.message_per_second = 60; |
||||
rate_limit.post = 6; |
||||
rate_limit.post_per_second = 600; |
||||
rate_limit.register = 3; |
||||
rate_limit.register_per_second = 3600; |
||||
rate_limit.image = 6; |
||||
rate_limit.image_per_second = 3600; |
||||
} // { |
||||
database = mapAttrs (name: mkDefault) { |
||||
user = "lemmy"; |
||||
host = "/run/postgresql"; |
||||
port = 5432; |
||||
database = "lemmy"; |
||||
pool_size = 5; |
||||
}; |
||||
}); |
||||
|
||||
services.postgresql = mkIf localPostgres { |
||||
enable = mkDefault true; |
||||
}; |
||||
|
||||
services.pict-rs.enable = true; |
||||
|
||||
services.caddy = mkIf cfg.caddy.enable { |
||||
enable = mkDefault true; |
||||
virtualHosts."${cfg.settings.hostname}" = { |
||||
extraConfig = '' |
||||
handle_path /static/* { |
||||
root * ${pkgs.lemmy-ui}/dist |
||||
file_server |
||||
} |
||||
@for_backend { |
||||
path /api/* /pictrs/* feeds/* nodeinfo/* |
||||
} |
||||
handle @for_backend { |
||||
reverse_proxy 127.0.0.1:${toString cfg.settings.port} |
||||
} |
||||
@post { |
||||
method POST |
||||
} |
||||
handle @post { |
||||
reverse_proxy 127.0.0.1:${toString cfg.settings.port} |
||||
} |
||||
@jsonld { |
||||
header Accept "application/activity+json" |
||||
header Accept "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" |
||||
} |
||||
handle @jsonld { |
||||
reverse_proxy 127.0.0.1:${toString cfg.settings.port} |
||||
} |
||||
handle { |
||||
reverse_proxy 127.0.0.1:${toString cfg.ui.port} |
||||
} |
||||
''; |
||||
}; |
||||
}; |
||||
|
||||
assertions = [{ |
||||
assertion = cfg.settings.database.createLocally -> localPostgres; |
||||
message = "if you want to create the database locally, you need to use a local database"; |
||||
}]; |
||||
|
||||
systemd.services.lemmy = { |
||||
description = "Lemmy server"; |
||||
|
||||
environment = { |
||||
LEMMY_CONFIG_LOCATION = "/run/lemmy/config.hjson"; |
||||
|
||||
# Verify how this is used, and don't put the password in the nix store |
||||
LEMMY_DATABASE_URL = with cfg.settings.database;"postgres:///${database}?host=${host}"; |
||||
}; |
||||
|
||||
documentation = [ |
||||
"https://join-lemmy.org/docs/en/administration/from_scratch.html" |
||||
"https://join-lemmy.org/docs" |
||||
]; |
||||
|
||||
wantedBy = [ "multi-user.target" ]; |
||||
|
||||
after = [ "pict-rs.service " ] ++ lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ]; |
||||
|
||||
requires = lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ]; |
||||
|
||||
# script is needed here since loadcredential is not accessible on ExecPreStart |
||||
script = '' |
||||
${pkgs.coreutils}/bin/install -m 600 ${settingsFormat.generate "config.hjson" cfg.settings} /run/lemmy/config.hjson |
||||
jwtSecret="$(< $CREDENTIALS_DIRECTORY/jwt_secret )" |
||||
${pkgs.jq}/bin/jq ".jwt_secret = \"$jwtSecret\"" /run/lemmy/config.hjson | ${pkgs.moreutils}/bin/sponge /run/lemmy/config.hjson |
||||
${pkgs.lemmy-server}/bin/lemmy_server |
||||
''; |
||||
|
||||
serviceConfig = { |
||||
DynamicUser = true; |
||||
RuntimeDirectory = "lemmy"; |
||||
LoadCredential = "jwt_secret:${cfg.jwtSecretPath}"; |
||||
}; |
||||
}; |
||||
|
||||
systemd.services.lemmy-ui = { |
||||
description = "Lemmy ui"; |
||||
|
||||
environment = { |
||||
LEMMY_UI_HOST = "127.0.0.1:${toString cfg.ui.port}"; |
||||
LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}"; |
||||
LEMMY_EXTERNAL_HOST = cfg.settings.hostname; |
||||
LEMMY_HTTPS = "false"; |
||||
}; |
||||
|
||||
documentation = [ |
||||
"https://join-lemmy.org/docs/en/administration/from_scratch.html" |
||||
"https://join-lemmy.org/docs" |
||||
]; |
||||
|
||||
wantedBy = [ "multi-user.target" ]; |
||||
|
||||
after = [ "lemmy.service" ]; |
||||
|
||||
requires = [ "lemmy.service" ]; |
||||
|
||||
serviceConfig = { |
||||
DynamicUser = true; |
||||
WorkingDirectory = "${pkgs.lemmy-ui}"; |
||||
ExecStart = "${pkgs.nodejs}/bin/node ${pkgs.lemmy-ui}/dist/js/server.js"; |
||||
}; |
||||
}; |
||||
|
||||
systemd.services.lemmy-postgresql = mkIf cfg.settings.database.createLocally { |
||||
description = "Lemmy postgresql db"; |
||||
after = [ "postgresql.service" ]; |
||||
bindsTo = [ "postgresql.service" ]; |
||||
requiredBy = [ "lemmy.service" ]; |
||||
partOf = [ "lemmy.service" ]; |
||||
script = with cfg.settings.database; '' |
||||
PSQL() { |
||||
${config.services.postgresql.package}/bin/psql --port=${toString cfg.settings.database.port} "$@" |
||||
} |
||||
# check if the database already exists |
||||
if ! PSQL -lqt | ${pkgs.coreutils}/bin/cut -d \| -f 1 | ${pkgs.gnugrep}/bin/grep -qw ${database} ; then |
||||
PSQL -tAc "CREATE ROLE ${user} WITH LOGIN;" |
||||
PSQL -tAc "CREATE DATABASE ${database} WITH OWNER ${user};" |
||||
fi |
||||
''; |
||||
serviceConfig = { |
||||
User = config.services.postgresql.superUser; |
||||
Type = "oneshot"; |
||||
RemainAfterExit = true; |
||||
}; |
||||
}; |
||||
}; |
||||
|
||||
} |
@ -0,0 +1,56 @@ |
||||
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-lemmy"> |
||||
<title>Lemmy</title> |
||||
<para> |
||||
Lemmy is a federated alternative to reddit in rust. |
||||
</para> |
||||
<section xml:id="module-services-lemmy-quickstart"> |
||||
<title>Quickstart</title> |
||||
<para> |
||||
the minimum to start lemmy is |
||||
</para> |
||||
<programlisting language="bash"> |
||||
services.lemmy = { |
||||
enable = true; |
||||
settings = { |
||||
hostname = "lemmy.union.rocks"; |
||||
database.createLocally = true; |
||||
}; |
||||
jwtSecretPath = "/run/secrets/lemmyJwt"; |
||||
caddy.enable = true; |
||||
} |
||||
</programlisting> |
||||
<para> |
||||
(note that you can use something like agenix to get your secret |
||||
jwt to the specified path) |
||||
</para> |
||||
<para> |
||||
this will start the backend on port 8536 and the frontend on port |
||||
1234. It will expose your instance with a caddy reverse proxy to |
||||
the hostname you’ve provided. Postgres will be initialized on that |
||||
same instance automatically. |
||||
</para> |
||||
</section> |
||||
<section xml:id="module-services-lemmy-usage"> |
||||
<title>Usage</title> |
||||
<para> |
||||
On first connection you will be asked to define an admin user. |
||||
</para> |
||||
</section> |
||||
<section xml:id="module-services-lemmy-missing"> |
||||
<title>Missing</title> |
||||
<itemizedlist spacing="compact"> |
||||
<listitem> |
||||
<para> |
||||
Exposing with nginx is not implemented yet. |
||||
</para> |
||||
</listitem> |
||||
<listitem> |
||||
<para> |
||||
This has been tested using a local database with a unix socket |
||||
connection. Using different database settings will likely |
||||
require modifications |
||||
</para> |
||||
</listitem> |
||||
</itemizedlist> |
||||
</section> |
||||
</chapter> |
Loading…
Reference in new issue