Delete/rename modules

This commit is contained in:
Andrea Ciceri 2024-10-17 23:16:22 +02:00
parent 3add3c5d49
commit 9dabfa7771
Signed by: aciceri
SSH key fingerprint: SHA256:/AagBweyV4Hlfg9u092n8hbHwD5fcB6A3qhDiDA65Rg
5 changed files with 8 additions and 594 deletions

View file

@ -1,5 +0,0 @@
PUBLIC_LOGIN_PAGE_MESSAGE=
IMMICH_WEB_URL=http://immich-web:3000
IMMICH_SERVER_URL=http://immich-server:3001
IMMICH_MACHINE_LEARNING_URL=http://immich-machine-learning:3003

View file

@ -1,584 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
hasAttr
hasPrefix
maintainers
mapAttrs
mkDefault
mkEnableOption
mkIf
mkMerge
mkOption
mkPackageOption
optional
optionalAttrs
optionalString
types
;
cfg = config.services.immich;
serverCfg = config.services.immich.server;
backendCfg = serverCfg.backend;
microservicesCfg = serverCfg.microservices;
webCfg = cfg.web;
mlCfg = cfg.machineLearning;
isServerPostgresUnix = hasPrefix "/" serverCfg.postgres.host;
postgresEnv =
if isServerPostgresUnix then
{
# If passwordFile is given, this will be overwritten in ExecStart
DB_URL = "socket://${serverCfg.postgres.host}?dbname=${serverCfg.postgres.database}";
}
else
{
DB_HOSTNAME = serverCfg.postgres.host;
DB_PORT = toString serverCfg.postgres.port;
DB_DATABASE_NAME = serverCfg.postgres.database;
DB_USERNAME = serverCfg.postgres.username;
};
typesenseEnv =
{
TYPESENSE_ENABLED = toString serverCfg.typesense.enable;
}
// optionalAttrs serverCfg.typesense.enable {
TYPESENSE_HOST = serverCfg.typesense.host;
TYPESENSE_PORT = toString serverCfg.typesense.port;
TYPESENSE_PROTOCOL = serverCfg.typesense.protocol;
};
# Don't start a redis instance if the user sets a custom redis connection
enableRedis =
!hasAttr "REDIS_URL" serverCfg.extraConfig && !hasAttr "REDIS_SOCKET" serverCfg.extraConfig;
redisServerCfg = config.services.redis.servers.immich;
redisEnv = optionalAttrs enableRedis {
REDIS_SOCKET = redisServerCfg.unixSocket;
};
serverEnv =
postgresEnv
// typesenseEnv
// redisEnv
// {
NODE_ENV = "production";
IMMICH_MEDIA_LOCATION = serverCfg.mediaDir;
IMMICH_MACHINE_LEARNING_URL =
if serverCfg.machineLearningUrl != null then serverCfg.machineLearningUrl else "false";
};
serverStartWrapper = program: ''
set -euo pipefail
mkdir -p ${serverCfg.mediaDir}
${optionalString (serverCfg.postgres.passwordFile != null) (
if isServerPostgresUnix then
''export DB_URL="socket://${serverCfg.postgres.username}:$(cat ${serverCfg.postgres.passwordFile})@${serverCfg.postgres.host}?dbname=${serverCfg.postgres.database}"''
else
"export DB_PASSWORD=$(cat ${serverCfg.postgres.passwordFile})"
)}
${optionalString serverCfg.typesense.enable ''
export TYPESENSE_API_KEY=$(cat ${serverCfg.typesense.apiKeyFile})
''}
exec ${program}
'';
commonServiceConfig = {
Restart = "on-failure";
# Hardening
CapabilityBoundingSet = "";
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateUsers = true;
PrivateTmp = true;
PrivateDevices = true;
PrivateMounts = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProcSubset = "pid";
# Would re-mount paths ignored by temporary root
# TODO ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"~@privileged"
"@pkey"
];
UMask = "0077";
};
serverServiceConfig = {
DynamicUser = true;
User = "immich";
Group = "immich";
SupplementaryGroups = optional enableRedis redisServerCfg.user;
StateDirectory = "immich";
StateDirectoryMode = "0750";
WorkingDirectory = "/var/lib/immich";
MemoryDenyWriteExecute = false; # nodejs requires this.
EnvironmentFile = mkIf (serverCfg.environmentFile != null) serverCfg.environmentFile;
TemporaryFileSystem = "/:ro";
BindReadOnlyPaths = [
"/nix/store"
"-/etc/resolv.conf"
"-/etc/nsswitch.conf"
"-/etc/hosts"
"-/etc/localtime"
"-/run/postgresql"
] ++ optional enableRedis redisServerCfg.unixSocket;
};
in
{
options.services.immich = {
enable = mkEnableOption "immich" // {
description = ''
Enables immich which consists of a backend server, microservices,
machine-learning and web ui. You can disable or reconfigure components
individually using the subsections.
'';
};
package = mkPackageOption pkgs "immich" { };
server = {
mediaDir = mkOption {
type = types.str;
default = "/var/lib/immich/media";
description = "Directory used to store media files.";
};
backend = {
enable = mkEnableOption "immich backend server" // {
default = true;
};
port = mkOption {
type = types.port;
default = 3001;
description = "Port to bind to.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified port.";
};
extraConfig = mkOption {
type = types.attrs;
default = { };
example = {
LOG_LEVEL = "debug";
};
description = ''
Extra configuration options (environment variables).
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with 'server' for available options.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Environment file as defined in systemd.exec(5). May be used to provide
additional secret variables to the service without adding them to the
world-readable Nix store.
'';
};
};
microservices = {
enable = mkEnableOption "immich microservices" // {
default = true;
};
port = mkOption {
type = types.port;
default = 3002;
description = "Port to bind to.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified port.";
};
extraConfig = mkOption {
type = types.attrs;
default = { };
example = {
REVERSE_GEOCODING_PRECISION = 1;
};
description = ''
Extra configuration options (environment variables).
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with 'microservices' for available options.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Environment file as defined in systemd.exec(5). May be used to provide
additional secret variables to the service without adding them to the
world-readable Nix store.
'';
};
};
typesense = {
enable = mkEnableOption "typesense" // {
default = true;
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
example = "typesense.example.com";
description = "Hostname/address of the typesense server to use.";
};
port = mkOption {
type = types.port;
default = 8108;
description = "The port of the typesense server to use.";
};
protocol = mkOption {
type = types.str;
default = "http";
description = "The protocol to use when connecting to the typesense server.";
};
apiKeyFile = mkOption {
type = types.path;
description = "Sets the api key for authentication with typesense.";
};
};
postgres = {
host = mkOption {
type = types.str;
default = "/run/postgresql";
description = "Hostname/address of the postgres server to use. If an absolute path is given here, it will be interpreted as a unix socket path.";
};
port = mkOption {
type = types.port;
default = 5432;
description = "The port of the postgres server to use.";
};
username = mkOption {
type = types.str;
default = "immich";
description = "The postgres username to use.";
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Sets the password for authentication with postgres.
May be unset when using socket authentication.
'';
};
database = mkOption {
type = types.str;
default = "immich";
description = "The postgres database to use.";
};
};
useMachineLearning = mkOption {
description = "Use the given machine learning server endpoint to enable ML functionality in immich.";
default = true;
type = types.bool;
};
machineLearningUrl = mkOption {
type = types.str;
default = "http://127.0.0.1:3003";
example = "https://immich-ml.internal.example.com";
description = "The machine learning server endpoint to use.";
};
extraConfig = mkOption {
type = types.attrs;
default = { };
example = {
REDIS_SOCKET = "/run/custom-redis";
};
description = ''
Extra configuration options (environment variables) for both backend and microservices.
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with both 'server' and 'microservices' for available options.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Environment file as defined in systemd.exec(5). May be used to provide
additional secret variables to the backend and microservices servers without
adding them to the world-readable Nix store.
'';
};
};
web = {
enable = mkEnableOption "immich web frontend" // {
default = true;
};
port = mkOption {
type = types.port;
default = 3000;
description = "Port to bind to.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified port.";
};
serverUrl = mkOption {
type = types.str;
default = "http://127.0.0.1:3001";
example = "https://immich-backend.internal.example.com";
description = "The backend server url to use.";
};
apiUrlExternal = mkOption {
type = types.str;
default = "/web";
description = "The api url to use for external requests.";
};
extraConfig = mkOption {
type = types.attrs;
default = { };
example = {
PUBLIC_LOGIN_PAGE_MESSAGE = "My awesome Immich instance!";
};
description = ''
Extra configuration options (environment variables).
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with 'web' for available options.
'';
};
};
machineLearning = {
enable = mkEnableOption "immich machine-learning server" // {
default = true;
};
port = mkOption {
type = types.port;
default = 3003;
description = "Port to bind to.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified port.";
};
extraConfig = mkOption {
type = types.attrs;
default = { };
example = {
MACHINE_LEARNING_MODEL_TTL = 600;
};
description = ''
Extra configuration options (environment variables).
Refer to [the documented variables](https://documentation.immich.app/docs/install/environment-variables) tagged with 'machine learning' for available options.
'';
};
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = !isServerPostgresUnix -> serverCfg.postgres.passwordFile != null;
message = "A database password must be provided when unix sockets are not used.";
}
];
networking.firewall.allowedTCPPorts = mkMerge [
(mkIf (backendCfg.enable && backendCfg.openFirewall) [ backendCfg.port ])
(mkIf (microservicesCfg.enable && microservicesCfg.openFirewall) [ microservicesCfg.port ])
(mkIf (webCfg.enable && webCfg.openFirewall) [ webCfg.port ])
(mkIf (mlCfg.enable && mlCfg.openFirewall) [ mlCfg.port ])
];
services.redis.servers.immich.enable = mkIf enableRedis true;
services.redis.vmOverCommit = mkIf enableRedis (mkDefault true);
systemd.services.immich-server = mkIf backendCfg.enable {
description = "Immich backend server (Self-hosted photo and video backup solution)";
after = [
"network.target"
"typesense.service"
"postgresql.service"
"immich-machine-learning.service"
] ++ optional enableRedis "redis-immich.service";
wantedBy = [ "multi-user.target" ];
environment =
serverEnv
// {
SERVER_PORT = toString backendCfg.port;
}
// mapAttrs (_: toString) serverCfg.extraConfig
// mapAttrs (_: toString) backendCfg.extraConfig;
script = serverStartWrapper "${cfg.package}/bin/server";
serviceConfig = mkMerge [
(commonServiceConfig // serverServiceConfig)
{
EnvironmentFile = mkIf (backendCfg.environmentFile != null) backendCfg.environmentFile;
}
];
};
systemd.services.immich-microservices = mkIf microservicesCfg.enable {
description = "Immich microservices (Self-hosted photo and video backup solution)";
after = [
"network.target"
"typesense.service"
"postgresql.service"
"immich-machine-learning.service"
] ++ optional enableRedis "redis-immich.service";
wantedBy = [ "multi-user.target" ];
environment =
serverEnv
// {
MICROSERVICES_PORT = toString microservicesCfg.port;
}
// mapAttrs (_: toString) serverCfg.extraConfig
// mapAttrs (_: toString) microservicesCfg.extraConfig;
script = serverStartWrapper "${cfg.package}/bin/microservices";
serviceConfig = mkMerge [
(commonServiceConfig // serverServiceConfig)
{
EnvironmentFile = mkIf (microservicesCfg.environmentFile != null) microservicesCfg.environmentFile;
}
];
};
systemd.services.immich-web = mkIf webCfg.enable {
description = "Immich web (Self-hosted photo and video backup solution)";
after = [
"network.target"
"immich-server.service"
];
wantedBy = [ "multi-user.target" ];
environment = {
NODE_ENV = "production";
PORT = toString webCfg.port;
IMMICH_SERVER_URL = webCfg.serverUrl;
IMMICH_API_URL_EXTERNAL = webCfg.apiUrlExternal;
} // mapAttrs (_: toString) webCfg.extraConfig;
script = ''
set -euo pipefail
export PUBLIC_IMMICH_SERVER_URL=$IMMICH_SERVER_URL
export PUBLIC_IMMICH_API_URL_EXTERNAL=$IMMICH_API_URL_EXTERNAL
exec ${cfg.package.web}/bin/web
'';
serviceConfig = commonServiceConfig // {
DynamicUser = true;
User = "immich-web";
Group = "immich-web";
MemoryDenyWriteExecute = false; # nodejs requires this.
TemporaryFileSystem = "/:ro";
BindReadOnlyPaths = [
"/nix/store"
"-/etc/resolv.conf"
"-/etc/nsswitch.conf"
"-/etc/hosts"
"-/etc/localtime"
];
};
};
systemd.services.immich-machine-learning = mkIf mlCfg.enable {
description = "Immich machine learning (Self-hosted photo and video backup solution)";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
environment = {
NODE_ENV = "production";
MACHINE_LEARNING_PORT = toString mlCfg.port;
MACHINE_LEARNING_CACHE_FOLDER = "/var/cache/immich-ml";
TRANSFORMERS_CACHE = "/var/cache/immich-ml";
} // mapAttrs (_: toString) mlCfg.extraConfig;
serviceConfig = commonServiceConfig // {
ExecStart = "${cfg.package.machine-learning}/bin/machine-learning";
DynamicUser = true;
User = "immich-ml";
Group = "immich-ml";
MemoryDenyWriteExecute = false; # onnxruntime_pybind11 requires this.
ProcSubset = "all"; # Needs /proc/cpuinfo
CacheDirectory = "immich-ml";
CacheDirectoryMode = "0700";
# TODO gpu access
TemporaryFileSystem = "/:ro";
BindReadOnlyPaths = [
"/nix/store"
"-/etc/resolv.conf"
"-/etc/nsswitch.conf"
"-/etc/hosts"
"-/etc/localtime"
];
};
};
meta.maintainers = with maintainers; [ oddlama ];
};
}

View file

@ -47,13 +47,16 @@
proxyWebsockets = true; proxyWebsockets = true;
}; };
}; };
"paper.aciceri.dev" = {
# "jellyfin.aciceri.dev" = { forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:${builtins.toString config.services.paperless.port}";
};
};
# "${config.services.nextcloud.hostName}" = {
# forceSSL = true; # forceSSL = true;
# enableACME = true; # enableACME = true;
# locations."/" = {
# proxyPass = "http://localhost:8096";
# };
# }; # };
# "sevenofnix.aciceri.dev" = { # "sevenofnix.aciceri.dev" = {
# forceSSL = true; # forceSSL = true;