nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 lib,
3 stdenv,
4 fetchFromGitHub,
5 fetchPnpmDeps,
6 pnpmConfigHook,
7 pnpm_10,
8 python3,
9 nodejs,
10 node-gyp,
11 runCommand,
12 nixosTests,
13 immich-machine-learning,
14 # build-time deps
15 pkg-config,
16 makeWrapper,
17 binaryen,
18 curl,
19 cacert,
20 extism-js,
21 unzip,
22 # runtime deps
23 cairo,
24 exiftool,
25 giflib,
26 jellyfin-ffmpeg, # Immich depends on the jellyfin customizations, see https://github.com/NixOS/nixpkgs/issues/351943
27 imagemagick,
28 libjpeg,
29 libpng,
30 libraw,
31 libheif,
32 librsvg,
33 pango,
34 perl,
35 pixman,
36 vips_8_17, # thumbnail generation fails with vips 8.18
37 buildPackages,
38}:
39let
40 pnpm = pnpm_10;
41
42 esbuild' = buildPackages.esbuild.override {
43 buildGoModule =
44 args:
45 buildPackages.buildGoModule (
46 args
47 // rec {
48 version = "0.25.5";
49 src = fetchFromGitHub {
50 owner = "evanw";
51 repo = "esbuild";
52 tag = "v${version}";
53 hash = "sha256-jemGZkWmN1x2+ZzJ5cLp3MoXO0oDKjtZTmZS9Be/TDw=";
54 };
55 vendorHash = "sha256-+BfxCyg0KkDQpHt/wycy/8CTG6YBA/VJvJFhhzUnSiQ=";
56 }
57 );
58 };
59
60 buildLock = {
61 sources =
62 map
63 (p: {
64 name = p.pname;
65 inherit (p) version;
66 inherit (p.src) rev;
67 })
68 [
69 imagemagick
70 libheif
71 libraw
72 ];
73
74 packages = [ ];
75 };
76
77 # The geodata website is not versioned, so we use the internet archive
78 geodata =
79 let
80 timestamp = "20250818205425";
81 date =
82 "${lib.substring 0 4 timestamp}-${lib.substring 4 2 timestamp}-${lib.substring 6 2 timestamp}T"
83 + "${lib.substring 8 2 timestamp}:${lib.substring 10 2 timestamp}:${lib.substring 12 2 timestamp}Z";
84 in
85 runCommand "immich-geodata"
86 {
87 outputHash = "sha256-zZHAomW1C4qReFbhme5dkVnTiLw+jmhZhzuYvoBVBCY=";
88 outputHashMode = "recursive";
89 nativeBuildInputs = [
90 cacert
91 curl
92 unzip
93 ];
94
95 meta.license = lib.licenses.cc-by-40;
96 }
97 ''
98 mkdir $out
99 url="https://web.archive.org/web/${timestamp}/http://download.geonames.org/export/dump"
100 curl -Lo ./cities500.zip "$url/cities500.zip"
101 curl -Lo $out/admin1CodesASCII.txt "$url/admin1CodesASCII.txt"
102 curl -Lo $out/admin2Codes.txt "$url/admin2Codes.txt"
103 curl -Lo $out/ne_10m_admin_0_countries.geojson \
104 https://github.com/nvkelso/natural-earth-vector/raw/ca96624a56bd078437bca8184e78163e5039ad19/geojson/ne_10m_admin_0_countries.geojson
105
106 unzip ./cities500.zip -d $out/
107 echo "${date}" > $out/geodata-date.txt
108 '';
109
110 # Without this thumbnail generation for raw photos fails with
111 # Error: Input file has corrupt header: tiff2vips: samples_per_pixel not a whole number of bytes
112 vips' = vips_8_17.overrideAttrs (prev: {
113 mesonFlags = prev.mesonFlags ++ [ "-Dtiff=disabled" ];
114 });
115in
116stdenv.mkDerivation (finalAttrs: {
117 pname = "immich";
118 version = "2.5.2";
119
120 src = fetchFromGitHub {
121 owner = "immich-app";
122 repo = "immich";
123 tag = "v${finalAttrs.version}";
124 hash = "sha256-e3gU2pSnbYQQU3SxGaJs8dwfTMpeGTz7dcFCmc7Pi/o=";
125 };
126
127 pnpmDeps = fetchPnpmDeps {
128 inherit (finalAttrs) pname version src;
129 inherit pnpm;
130 fetcherVersion = 3;
131 hash = "sha256-+2rm184Hb+S75VyDn3FU36M/3SH2hUDYbKotIGopmvU=";
132 };
133
134 postPatch = ''
135 # pg_dumpall fails without database root access
136 # see https://github.com/immich-app/immich/issues/13971
137 substituteInPlace server/src/utils/database-backups.ts \
138 --replace-fail '`/usr/lib/postgresql/''${databaseMajorVersion}/bin/''${bin}`' '`''${bin}`'
139 '';
140
141 nativeBuildInputs = [
142 nodejs
143 pkg-config
144 pnpmConfigHook
145 pnpm
146 python3
147 makeWrapper
148 node-gyp # for building node_modules/sharp from source
149 ];
150
151 buildInputs = [
152 jellyfin-ffmpeg
153 imagemagick
154 libraw
155 libheif
156 # https://github.com/Automattic/node-canvas/blob/master/Readme.md#compiling
157 cairo
158 giflib
159 libjpeg
160 libpng
161 librsvg
162 pango
163 pixman
164 # Required for sharp
165 vips'
166 ];
167
168 env.SHARP_FORCE_GLOBAL_LIBVIPS = 1;
169 env.ESBUILD_BINARY_PATH = lib.getExe esbuild';
170 # fix for node-gyp, see https://github.com/nodejs/node-gyp/issues/1191#issuecomment-301243919
171 env.npm_config_nodedir = nodejs;
172
173 buildPhase = ''
174 runHook preBuild
175
176 # If exiftool-vendored.pl isn't found, exiftool is searched for on the PATH
177 rm node_modules/.pnpm/node_modules/exiftool-vendored.pl
178
179 pnpm --filter immich build
180
181 runHook postBuild
182 '';
183
184 installPhase = ''
185 runHook preInstall
186
187 local -r packageOut="$out/lib/node_modules/immich"
188
189 # install node_modules and built files in $out
190 # upstream uses pnpm deploy to build their docker images
191 pnpm --filter immich deploy --prod --no-optional "$packageOut"
192
193 # remove build artifacts that bloat the closure
194 find "$packageOut/node_modules" \( \
195 -name config.gypi \
196 -o -name .deps \
197 -o -name '*Makefile' \
198 -o -name '*.target.mk' \
199 \) -exec rm -r {} +
200
201 mkdir -p "$packageOut/build"
202 ln -s '${finalAttrs.passthru.plugins}' "$packageOut/build/corePlugin"
203 ln -s '${finalAttrs.passthru.web}' "$packageOut/build/www"
204 ln -s '${geodata}' "$packageOut/build/geodata"
205
206 echo '${builtins.toJSON buildLock}' > "$packageOut/build/build-lock.json"
207
208 makeWrapper '${lib.getExe nodejs}' "$out/bin/immich-admin" \
209 --add-flags "$packageOut/dist/main" \
210 --add-flags immich-admin
211 makeWrapper '${lib.getExe nodejs}' "$out/bin/server" \
212 --add-flags "$packageOut/dist/main" \
213 --chdir "$packageOut" \
214 --set IMMICH_BUILD_DATA "$packageOut/build" \
215 --set NODE_ENV production \
216 --suffix PATH : '${
217 lib.makeBinPath [
218 exiftool
219 jellyfin-ffmpeg
220 perl # exiftool-vendored checks for Perl even if exiftool comes from $PATH
221 ]
222 }'
223
224 runHook postInstall
225 '';
226
227 passthru = {
228 tests = {
229 inherit (nixosTests) immich immich-vectorchord-migration immich-vectorchord-reindex;
230 };
231
232 machine-learning = immich-machine-learning.override {
233 immich = finalAttrs.finalPackage;
234 };
235
236 plugins = stdenv.mkDerivation {
237 pname = "immich-plugins";
238 inherit (finalAttrs) version src pnpmDeps;
239
240 nativeBuildInputs = [
241 binaryen
242 extism-js
243 nodejs
244 pnpmConfigHook
245 pnpm
246 ];
247
248 buildPhase = ''
249 runHook preBuild
250
251 pnpm --filter plugins build
252
253 runHook postBuild
254 '';
255
256 installPhase = ''
257 runHook preInstall
258
259 cd plugins
260 mkdir $out
261 cp -r dist manifest.json $out
262
263 runHook postInstall
264 '';
265 };
266
267 web = stdenv.mkDerivation {
268 pname = "immich-web";
269 inherit (finalAttrs) version src pnpmDeps;
270
271 nativeBuildInputs = [
272 nodejs
273 pnpmConfigHook
274 pnpm
275 ];
276
277 buildPhase = ''
278 runHook preBuild
279
280 pnpm --filter @immich/sdk build
281 pnpm --filter immich-web build
282
283 runHook postBuild
284 '';
285
286 installPhase = ''
287 runHook preInstall
288
289 cd web
290 cp -r build $out
291
292 runHook postInstall
293 '';
294 };
295
296 inherit
297 geodata
298 pnpm
299 ;
300 };
301
302 meta = {
303 changelog = "https://github.com/immich-app/immich/releases/tag/${finalAttrs.src.tag}";
304 description = "Self-hosted photo and video backup solution";
305 homepage = "https://immich.app/";
306 license = with lib.licenses; [
307 agpl3Only
308 cc-by-40 # geonames
309 ];
310 maintainers = with lib.maintainers; [
311 dotlambda
312 jvanbruegge
313 Scrumplex
314 titaniumtown
315 ];
316 platforms = lib.platforms.linux ++ lib.platforms.freebsd;
317 mainProgram = "server";
318 };
319})