Immich Is Saving Your Library to the System Disk Instead of Your Data Pool
You set up Immich, pointed it at a big data drive, and yet your operating-system disk keeps filling up as photos come in. The system partition creeps toward full while the roomy pool you bought for photos stays nearly empty. The cause is almost always the same: the folder Immich actually writes to is decided by a single volume mapping in its Docker Compose file — and that mapping is pointing at a config folder on the system disk, not the data location your installer thinks it configured.
Rather not hand-edit YAML? SparkBox puts every app's storage on your data pool by design and ships fixes like this in its update stream. See the walkthrough →
The 10-second version: Immich writes its library to whatever host folder is bound to /usr/src/app/upload in the immich-server container. Change that bind mount to a folder on your large data disk, move the existing files, and restart. A PHOTOS_PATH or data-directory variable elsewhere is ignored unless the volume actually references it.
What "the library" means in Immich
Immich stores several kinds of data under one host folder that it mounts at /usr/src/app/upload inside the immich-server and immich-microservices containers. Inside that folder you'll normally see subdirectories like library (your originals), upload (files still being processed), thumbs (thumbnails), encoded-video, and profile. When people say "my Immich library is on the wrong disk," they mean this whole folder tree landed on the system partition.
A quick vocabulary note, because it matters here:
- System disk — the small, fast drive that holds your operating system and app configuration. Often only tens of gigabytes free.
- Data pool — the large drive or array you set aside for bulk files like photos and video.
- Bind mount / volume mapping — a line in Docker Compose of the form
host_path:container_path. It tells the container "when I write to container_path, actually put the bytes at host_path on the machine."
Why this happens: the volume points at a config folder
This is a genuine wiring bug, and it's worth understanding because the fix follows directly from it. In an affected setup, the Immich compose file hardcodes the upload bind mount to a config directory that lives on the system disk — something structurally like:
${SB_ROOT:-/opt/sparkbox}/modules/immich/config/upload:/usr/src/app/upload
Meanwhile the installer seeded a perfectly good data-pool variable — PHOTOS_PATH=${SB_DATA_DIR}/photos — and even added it to the config allowlist. But nothing in the compose file ever references that variable. So Immich dutifully writes to the config folder on the system disk, and the data-pool path just sits there unused.
If that sounds familiar, it's the exact same class of mistake as the Nextcloud-on-system-disk bug that was fixed in SparkBox v1.6.135: an app was configured to store bulk data, the correct destination existed, but the container's volume never pointed to it. On SparkBox this specific Immich mapping fix is in progress with the lead dev — until it ships, the manual fix below works on any Immich install, SparkBox or not.
Gotcha: Changing PHOTOS_PATH, UPLOAD_LOCATION, or any environment variable does nothing on its own if the compose volume line has a hardcoded path. The volume line is authoritative. Always verify it directly.
Fix 1 — Point the upload volume at your data pool
This is the core fix. You're redirecting the one mapping that decides where the library lives.
- Confirm the current path. Open your Immich
docker-compose.ymland find the line ending in:/usr/src/app/uploadunder theimmich-serverservice (and the same line underimmich-microservicesif present). The part before the colon is where your photos are going right now. - Stop the stack so nothing is writing while you move data:
docker compose down - Move the existing library to the data pool. Copy the entire folder — with all subdirectories intact — to a location on your large disk. Copy first, verify, then delete the old copy; don't move-and-hope:
Use whatever your real old and new paths are. Thecp -a /old/config/upload/. /data/photos/-aflag preserves permissions and the folder structure, which Immich depends on. - Update both volume lines to the new host path:
Make sure the/data/photos:/usr/src/app/uploadimmich-serverandimmich-microservicesservices use the same host path — they share the library. - Start the stack again:
docker compose up -d - Verify it took. Upload a test photo, then check that the new file appears under the data-pool folder and that the system disk is no longer growing:
du -sh /data/photos
Because Immich stores paths relative to the upload root, moving the folder with its structure intact means your existing photos, thumbnails, and encoded videos are all found again — no re-upload, no re-processing from your phone.
Fix 2 — Keep the change from being overwritten by updates
If an app manager regenerates docker-compose.yml on update, your edited line can be reverted, silently sending new photos back to the system disk. Two durable options:
- Use a compose override. Create a
docker-compose.override.ymlnext to the main file that redefines only the upload volume for the two Immich services. Overrides are merged on top and usually survive regeneration of the base file. - Re-check after every major upgrade. Make "confirm the
/usr/src/app/uploadmapping still points at the data pool" part of your post-update checklist. A five-secondgrepsaves a full disk later:grep upload docker-compose.yml
If the system disk already filled up: a full system partition can stop containers from starting cleanly. Free space first — move the upload folder off the disk as in Fix 1 — before trying to bring the stack back up.
Related bugs fixed in the same batch
The Immich hardware-acceleration toggle and this storage wiring were part of a broader bugfix batch. A few neighbours you may run into on the same box:
- Stranded containers you couldn't delete. Failed installs used to leave behind containers set to
restart: alwaysthat a normal delete couldn't remove — they just came back. Removal is now stop-tolerant and forces the container down first (issue #26183). If you have a zombie Immich container from a botched install, stopping it before removal is the manual equivalent. - Alerts that reappeared after "Clear." Clearing an alert used to only wipe it from the browser's local storage, so it returned on the next load. Clearing now issues a proper server-side delete, so a dismissed alert stays dismissed.
- Calibre-Web rejecting an empty library folder. On first run Calibre-Web refused folders without a
metadata.db; an empty library is now seeded automatically so first boot doesn't error out (issue #25928). Not Immich, but the same batch — worth knowing if you run both.
Frequently asked
Why is Immich storing my photos on the system disk?
Because the host folder bound to /usr/src/app/upload in the immich-server container lives on your system disk. Immich writes everything there. Any separate "photos path" variable is irrelevant unless that exact volume line uses it.
Where does Immich actually keep the library?
Whatever the host side of the /usr/src/app/upload mapping is. Point it at a folder on your large data disk and that's where the library lives — originals, thumbnails, encoded video and all.
Can I move an existing Immich library without losing photos?
Yes. Stop the stack, copy the whole upload folder with its subfolders to the new disk, update the volume mapping to the new host path, and start again. Immich uses relative paths internally, so it reindexes in place without re-uploading anything.
Will this happen again after an update?
It can if an upgrade regenerates the compose file with the hardcoded path. Use a docker-compose.override.yml for the upload volume, or re-check the mapping after each major update.
Skip the volume-mapping detective work
SparkBox puts Immich's photo library on your data pool by default and ships wiring fixes like this one through its update stream — no YAML archaeology required.