nixpkgs/nixos/modules/services/web-apps/karakeep.nix
2025-04-21 20:20:20 -04:00

225 lines
6.4 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.karakeep;
karakeepEnv = lib.mkMerge [
{ DATA_DIR = "/var/lib/karakeep"; }
(lib.mkIf cfg.meilisearch.enable {
MEILI_ADDR = "http://127.0.0.1:${toString config.services.meilisearch.listenPort}";
})
(lib.mkIf cfg.browser.enable {
BROWSER_WEB_URL = "http://127.0.0.1:${toString cfg.browser.port}";
})
cfg.extraEnvironment
];
environmentFiles = [
"/var/lib/karakeep/settings.env"
] ++ (lib.optional (cfg.environmentFile != null) cfg.environmentFile);
in
{
options = {
services.karakeep = {
enable = lib.mkEnableOption "Enable the Karakeep service";
package = lib.mkPackageOption pkgs "karakeep" { };
extraEnvironment = lib.mkOption {
description = ''
Environment variables to pass to Karakaeep. This is how most settings
can be configured. Changing DATA_DIR is possible but not supported.
See https://docs.karakeep.app/configuration/
'';
type = lib.types.attrsOf lib.types.str;
default = { };
example = lib.literalExpression ''
{
PORT = "1234";
DISABLE_SIGNUPS = "true";
DISABLE_NEW_RELEASE_CHECK = "true";
}
'';
};
environmentFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = ''
An optional path to an environment file that will be used in the web and workers
services. This is useful for loading private keys.
'';
example = "/var/lib/karakeep/secrets.env";
};
browser = {
enable = lib.mkOption {
description = ''
Enable the karakeep-browser service that runs a chromium instance in
the background with debugging ports exposed. This is necessary for
certain features like screenshots.
'';
type = lib.types.bool;
default = true;
};
port = lib.mkOption {
description = "The port the browser should run on.";
type = lib.types.port;
default = 9222;
};
exe = lib.mkOption {
description = "The browser executable (must be Chrome-like).";
type = lib.types.str;
default = "${pkgs.chromium}/bin/chromium";
defaultText = lib.literalExpression "\${pkgs.chromium}/bin/chromium";
example = lib.literalExpression "\${pkgs.google-chrome}/bin/google-chrome-stable";
};
};
meilisearch = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Enable Meilisearch and configure Karakeep to use it. Meilisearch is
required for text search.
'';
};
};
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
users.groups.karakeep = { };
users.users.karakeep = {
isSystemUser = true;
group = "karakeep";
};
services.meilisearch = lib.mkIf cfg.meilisearch.enable {
enable = true;
};
systemd.services.karakeep-init = {
description = "Initialize Karakeep Data";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
partOf = [ "karakeep.service" ];
path = [ pkgs.openssl ];
script = ''
umask 0077
if [ ! -f "$STATE_DIRECTORY/settings.env" ]; then
cat <<EOF >"$STATE_DIRECTORY/settings.env"
# Generated by NixOS Karakeep module
MEILI_MASTER_KEY=$(openssl rand -base64 36)
NEXTAUTH_SECRET=$(openssl rand -base64 36)
EOF
fi
export DATA_DIR="$STATE_DIRECTORY"
exec "${cfg.package}/lib/karakeep/migrate"
'';
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
User = "karakeep";
Group = "karakeep";
StateDirectory = "karakeep";
PrivateTmp = "yes";
};
};
systemd.services.karakeep-workers = {
description = "Karakeep Workers";
wantedBy = [ "multi-user.target" ];
after = [
"network.target"
"karakeep-init.service"
];
partOf = [ "karakeep.service" ];
path = [
pkgs.monolith
pkgs.yt-dlp
];
environment = karakeepEnv;
serviceConfig = {
User = "karakeep";
Group = "karakeep";
ExecStart = "${cfg.package}/lib/karakeep/start-workers";
StateDirectory = "karakeep";
EnvironmentFile = environmentFiles;
PrivateTmp = "yes";
};
};
systemd.services.karakeep-web = {
description = "Karakeep Web";
wantedBy = [ "multi-user.target" ];
after = [
"network.target"
"karakeep-init.service"
"karakeep-workers.service"
];
partOf = [ "karakeep.service" ];
environment = karakeepEnv;
serviceConfig = {
ExecStart = "${cfg.package}/lib/karakeep/start-web";
User = "karakeep";
Group = "karakeep";
StateDirectory = "karakeep";
EnvironmentFile = environmentFiles;
PrivateTmp = "yes";
};
};
systemd.services.karakeep-browser = lib.mkIf cfg.browser.enable {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
partOf = [ "karakeep.service" ];
script = ''
export HOME="$CACHE_DIRECTORY"
exec ${cfg.browser.exe} \
--headless --no-sandbox --disable-gpu --disable-dev-shm-usage \
--remote-debugging-address=127.0.0.1 \
--remote-debugging-port=${toString cfg.browser.port} \
--hide-scrollbars \
--user-data-dir="$STATE_DIRECTORY"
'';
serviceConfig = {
Type = "simple";
Restart = "on-failure";
CacheDirectory = "karakeep-browser";
StateDirectory = "karakeep-browser";
DevicePolicy = "closed";
DynamicUser = true;
LockPersonality = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = "strict";
RestrictNamespaces = true;
RestrictRealtime = true;
};
};
};
meta = {
maintainers = [ lib.maintainers.three ];
};
}