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 = "Port of the postgresql server.";
159 description = "The database user for immich.";
163 enable = mkEnableOption "a redis cache for use with immich" // {
168 default = config.services.redis.servers.immich.unixSocket;
169 defaultText = lib.literalExpression "config.services.redis.servers.immich.unixSocket";
170 description = "The host that redis will listen on.";
175 description = "The port that redis will listen on. Set to zero to disable TCP.";
180 config = mkIf cfg.enable {
183 assertion = !isPostgresUnixSocket -> cfg.secretsFile != null;
184 message = "A secrets file containing at least the database password must be provided when unix sockets are not used.";
188 services.postgresql = mkIf cfg.database.enable {
190 ensureDatabases = mkIf cfg.database.createDB [ cfg.database.name ];
191 ensureUsers = mkIf cfg.database.createDB [
193 name = cfg.database.user;
194 ensureDBOwnership = true;
195 ensureClauses.login = true;
198 extraPlugins = ps: with ps; [ pgvecto-rs ];
200 shared_preload_libraries = [ "vectors.so" ];
201 search_path = "\"$user\", public, vectors";
204 systemd.services.postgresql.serviceConfig.ExecStartPost =
206 sqlFile = pkgs.writeText "immich-pgvectors-setup.sql" ''
207 CREATE EXTENSION IF NOT EXISTS unaccent;
208 CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
209 CREATE EXTENSION IF NOT EXISTS vectors;
210 CREATE EXTENSION IF NOT EXISTS cube;
211 CREATE EXTENSION IF NOT EXISTS earthdistance;
212 CREATE EXTENSION IF NOT EXISTS pg_trgm;
214 ALTER SCHEMA public OWNER TO ${cfg.database.user};
215 ALTER SCHEMA vectors OWNER TO ${cfg.database.user};
216 GRANT SELECT ON TABLE pg_vector_index_stat TO ${cfg.database.user};
218 ALTER EXTENSION vectors UPDATE;
223 ${lib.getExe' config.services.postgresql.package "psql"} -d "${cfg.database.name}" -f "${sqlFile}"
227 services.redis.servers = mkIf cfg.redis.enable {
231 port = cfg.redis.port;
232 bind = mkIf (!isRedisUnixSocket) cfg.redis.host;
236 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
238 services.immich.environment =
241 if isPostgresUnixSocket then
242 { DB_URL = "socket://${cfg.database.host}?dbname=${cfg.database.name}"; }
245 DB_HOSTNAME = cfg.database.host;
246 DB_PORT = toString cfg.database.port;
247 DB_DATABASE_NAME = cfg.database.name;
248 DB_USERNAME = cfg.database.user;
251 if isRedisUnixSocket then
252 { REDIS_SOCKET = cfg.redis.host; }
255 REDIS_PORT = toString cfg.redis.port;
256 REDIS_HOSTNAME = cfg.redis.host;
263 IMMICH_PORT = toString cfg.port;
264 IMMICH_MEDIA_LOCATION = cfg.mediaLocation;
265 IMMICH_MACHINE_LEARNING_URL = "http://localhost:3003";
268 services.immich.machine-learning.environment = {
269 MACHINE_LEARNING_WORKERS = "1";
270 MACHINE_LEARNING_WORKER_TIMEOUT = "120";
271 MACHINE_LEARNING_CACHE_FOLDER = "/var/cache/immich";
272 IMMICH_HOST = "localhost";
273 IMMICH_PORT = "3003";
276 systemd.services.immich-server = {
277 description = "Immich backend server (Self-hosted photo and video backup solution)";
278 after = [ "network.target" ];
279 wantedBy = [ "multi-user.target" ];
280 inherit (cfg) environment;
282 serviceConfig = commonServiceConfig // {
283 ExecStart = lib.getExe cfg.package;
284 EnvironmentFile = mkIf (cfg.secretsFile != null) cfg.secretsFile;
285 StateDirectory = "immich";
286 RuntimeDirectory = "immich";
292 systemd.services.immich-machine-learning = mkIf cfg.machine-learning.enable {
293 description = "immich machine learning";
294 after = [ "network.target" ];
295 wantedBy = [ "multi-user.target" ];
296 inherit (cfg.machine-learning) environment;
297 serviceConfig = commonServiceConfig // {
298 ExecStart = lib.getExe (cfg.package.machine-learning.override { immich = cfg.package; });
299 CacheDirectory = "immich";
305 users.users = mkIf (cfg.user == "immich") {
312 users.groups = mkIf (cfg.group == "immich") { immich = { }; };
314 meta.maintainers = with lib.maintainers; [ jvanbruegge ];