8 cfg = config.services.immich;
9 isPostgresUnixSocket = lib.hasPrefix "/" cfg.database.host;
10 isRedisUnixSocket = lib.hasPrefix "/" cfg.redis.host;
12 commonServiceConfig = {
14 Restart = "on-failure";
18 CapabilityBoundingSet = "";
19 NoNewPrivileges = true;
22 PrivateDevices = true;
25 ProtectControlGroups = true;
27 ProtectHostname = true;
28 ProtectKernelLogs = true;
29 ProtectKernelModules = true;
30 ProtectKernelTunables = true;
31 RestrictAddressFamilies = [
36 RestrictNamespaces = true;
37 RestrictRealtime = true;
38 RestrictSUIDSGID = true;
48 options.services.immich = {
49 enable = mkEnableOption "Immich";
50 package = lib.mkPackageOption pkgs "immich" { };
52 mediaLocation = mkOption {
54 default = "/var/lib/immich";
55 description = "Directory used to store media files. If it is not the default, the directory has to be created manually such that the immich user is able to read and write to it.";
57 environment = mkOption {
58 type = types.submodule { freeformType = types.attrsOf types.str; };
61 IMMICH_LOG_LEVEL = "verbose";
64 Extra configuration environment variables. Refer to the [documentation](https://immich.app/docs/install/environment-variables) for options tagged with 'server', 'api' or 'microservices'.
67 secretsFile = mkOption {
71 # We don't want users to be able to pass a path literal here but
72 # it should look like a path.
73 check = it: lib.isString it && lib.types.path.check it;
77 example = "/run/secrets/immich";
79 Path of a file with extra environment variables to be loaded from disk. This file is not added to the nix store, so it can be used to pass secrets to immich. Refer to the [documentation](https://immich.app/docs/install/environment-variables) for options.
81 To set a database password set this to a file containing:
89 default = "localhost";
90 description = "The host that immich will listen on.";
95 description = "The port that immich will listen on.";
97 openFirewall = mkOption {
100 description = "Whether to open the immich port in the firewall";
105 description = "The user immich should run as.";
110 description = "The group immich should run as.";
115 mkEnableOption "immich's machine-learning functionality to detect faces and search for objects"
119 environment = mkOption {
120 type = types.submodule { freeformType = types.attrsOf types.str; };
123 MACHINE_LEARNING_MODEL_TTL = "600";
126 Extra configuration environment variables. Refer to the [documentation](https://immich.app/docs/install/environment-variables) for options tagged with 'machine-learning'.
133 mkEnableOption "the postgresql database for use with immich. See {option}`services.postgresql`"
137 createDB = mkEnableOption "the automatic creation of the database for immich." // {
143 description = "The name of the immich database.";
147 default = "/run/postgresql";
148 example = "127.0.0.1";
149 description = "Hostname or address of the postgresql server. If an absolute path is given here, it will be interpreted as a unix socket path.";
154 description = "The database user for immich.";
158 enable = mkEnableOption "a redis cache for use with immich" // {
163 default = config.services.redis.servers.immich.unixSocket;
164 defaultText = lib.literalExpression "config.services.redis.servers.immich.unixSocket";
165 description = "The host that redis will listen on.";
170 description = "The port that redis will listen on. Set to zero to disable TCP.";
175 config = mkIf cfg.enable {
178 assertion = !isPostgresUnixSocket -> cfg.secretsFile != null;
179 message = "A secrets file containing at least the database password must be provided when unix sockets are not used.";
183 services.postgresql = mkIf cfg.database.enable {
185 ensureDatabases = mkIf cfg.database.createDB [ cfg.database.name ];
186 ensureUsers = mkIf cfg.database.createDB [
188 name = cfg.database.user;
189 ensureDBOwnership = true;
190 ensureClauses.login = true;
193 extraPlugins = ps: with ps; [ pgvecto-rs ];
195 shared_preload_libraries = [ "vectors.so" ];
196 search_path = "\"$user\", public, vectors";
199 systemd.services.postgresql.serviceConfig.ExecStartPost =
201 sqlFile = pkgs.writeText "immich-pgvectors-setup.sql" ''
202 CREATE EXTENSION IF NOT EXISTS unaccent;
203 CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
204 CREATE EXTENSION IF NOT EXISTS vectors;
205 CREATE EXTENSION IF NOT EXISTS cube;
206 CREATE EXTENSION IF NOT EXISTS earthdistance;
207 CREATE EXTENSION IF NOT EXISTS pg_trgm;
209 ALTER SCHEMA public OWNER TO ${cfg.database.user};
210 ALTER SCHEMA vectors OWNER TO ${cfg.database.user};
211 GRANT SELECT ON TABLE pg_vector_index_stat TO ${cfg.database.user};
213 ALTER EXTENSION vectors UPDATE;
218 ${lib.getExe' config.services.postgresql.package "psql"} -d "${cfg.database.name}" -f "${sqlFile}"
222 services.redis.servers = mkIf cfg.redis.enable {
226 port = cfg.redis.port;
227 bind = mkIf (!isRedisUnixSocket) cfg.redis.host;
231 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
233 services.immich.environment =
236 if isPostgresUnixSocket then
237 { DB_URL = "socket://${cfg.database.host}?dbname=${cfg.database.name}"; }
240 DB_HOSTNAME = cfg.database.host;
241 DB_PORT = toString cfg.database.port;
242 DB_DATABASE_NAME = cfg.database.name;
243 DB_USERNAME = cfg.database.user;
246 if isRedisUnixSocket then
247 { REDIS_SOCKET = cfg.redis.host; }
250 REDIS_PORT = toString cfg.redis.port;
251 REDIS_HOSTNAME = cfg.redis.host;
258 IMMICH_PORT = toString cfg.port;
259 IMMICH_MEDIA_LOCATION = cfg.mediaLocation;
260 IMMICH_MACHINE_LEARNING_URL = "http://localhost:3003";
263 services.immich.machine-learning.environment = {
264 MACHINE_LEARNING_WORKERS = "1";
265 MACHINE_LEARNING_WORKER_TIMEOUT = "120";
266 MACHINE_LEARNING_CACHE_FOLDER = "/var/cache/immich";
267 IMMICH_HOST = "localhost";
268 IMMICH_PORT = "3003";
271 systemd.services.immich-server = {
272 description = "Immich backend server (Self-hosted photo and video backup solution)";
273 after = [ "network.target" ];
274 wantedBy = [ "multi-user.target" ];
275 inherit (cfg) environment;
277 serviceConfig = commonServiceConfig // {
278 ExecStart = lib.getExe cfg.package;
279 EnvironmentFile = mkIf (cfg.secretsFile != null) cfg.secretsFile;
280 StateDirectory = "immich";
281 RuntimeDirectory = "immich";
287 systemd.services.immich-machine-learning = mkIf cfg.machine-learning.enable {
288 description = "immich machine learning";
289 after = [ "network.target" ];
290 wantedBy = [ "multi-user.target" ];
291 inherit (cfg.machine-learning) environment;
292 serviceConfig = commonServiceConfig // {
293 ExecStart = lib.getExe (cfg.package.machine-learning.override { immich = cfg.package; });
294 CacheDirectory = "immich";
300 users.users = mkIf (cfg.user == "immich") {
307 users.groups = mkIf (cfg.group == "immich") { immich = { }; };
309 meta.maintainers = with lib.maintainers; [ jvanbruegge ];