Self-Hosting
Immich photos won't back up
Fix an Immich phone that won't upload — the foreground/background split, iOS Background App Refresh, Android battery killers, Wi-Fi-only default — plus the DB+library backup that actually restores.
Problem summary
An Immich phone that 'isn't backing up' is almost always a mobile-OS background-task problem, not a server problem: iOS Background App Refresh off, an Android vendor battery-saver killing the worker, the Wi-Fi-only default, or confusing the foreground and background uploaders (they're separate mechanisms that don't hand off). The deeper operator trap is backup itself — Immich stores asset paths in Postgres and does NOT rescan the library to rebuild the DB, so a restore that has the photo files but no matching `pg_dump` leaves you with orphaned files and an empty timeline. Back up both the database and the `library`/`upload`/`profile` folders, kept consistent.
Open the app to the foreground and watch a manual backup run.
docker exec -t immich_postgres pg_dump --clean --if-exists --dbname=immich --username=postgres | gzip > /backup/immich-dump.sql.gz
Assets upload while the app is open (foreground path works).
If files and DB drift apart, the restore will show orphaned assets.
Layer path
Step-by-step runbook
Start here. Do each check in order, compare it to the expected result, and stop when the evidence explains the failure or the safe stop point applies.
Reproduce in the foreground
Check: Open Immich and run a manual/foreground backup.
Expected result: Assets upload while the app is open.
If not: If it fails here, treat it as connectivity/login/version, not background throttling.
Fix the OS background path
Check: Enable iOS Background App Refresh or exempt the Android app from battery optimization.
Expected result: The background worker runs after the app is backgrounded.
If not: If the OS still throttles, rely on foreground for large imports.
Check network + album scope
Check: Confirm cellular is allowed (if wanted) and the album is selected.
Expected result: The selected albums upload on your current network.
If not: Enable cellular and select the album, then re-enter the backup page to refresh counts.
Set up the database dump
Check: Schedule the pg_dump (or the prodrigestivill/postgres-backup-local sidecar).
Expected result: A fresh, non-empty dump exists alongside the library backup.
If not: Fix container name / DB creds if the dump errors or is empty.
Back up the originals consistently
Check: Back up library/, upload/, profile/ in the same window as the DB dump.
Expected result: The DB dump and the asset folders are from a consistent point in time.
If not: Quiesce or snapshot if assets change during the copy.
Safe stop: If files and DB drift apart, the restore will show orphaned assets.
Prove the restore
Check: Restore the DB + originals onto a scratch stack and open the timeline.
Expected result: Assets appear with a populated timeline, not orphaned files.
If not: If the timeline is empty, the DB and library weren't a matching pair — restore a consistent set.
Decision tree
If: Foreground upload also fails
Then: Not a background-throttle problem — it's connectivity, login, or version.
Action: Check the server URL/TLS, re-login, and match app/server versions before touching OS settings.
If: Foreground works, background doesn't (iOS)
Then: iOS is not running the background task.
Action: Enable Background App Refresh; for reliable bulk imports use foreground — iOS background timing is not guaranteed.
If: Foreground works, background doesn't (Android)
Then: A vendor battery policy is killing the worker.
Action: Exempt Immich from battery optimization and any 'deep sleep'/app-kill list, then retest.
If: Uploads stop off-Wi-Fi
Then: The Wi-Fi-only default is in effect.
Action: Allow cellular in backup settings if that's intended.
If: Assets uploaded but missing after a restore
Then: DB/file-set mismatch — files exist without matching database rows.
Action: Restore the matching pg_dump; never restore the library without its database.
Safe stop: Stop and restore from a consistent DB+library pair rather than re-importing blindly.
Evidence table
| Symptom | Evidence to collect | Likely layer | Next action |
|---|---|---|---|
| Backup page 'remaining' count never decreases in the background | Whether foreground upload moves the same count | Mobile OS background-task scheduling | Fix Background App Refresh (iOS) / battery exemption (Android); use foreground for bulk. |
| Uploads work at home, stop on mobile data | The network type when it stalls | Wi-Fi-only backup setting | Enable cellular uploads in settings if intended. |
| App can't log in or sync after an update | App version vs server version | Version skew (major+minor) | Match app and server on the same v2.x line. |
| Restored Immich shows files on disk but an empty timeline | Whether a matching pg_dump was restored with the library | Database/library inconsistency | Restore the consistent DB dump; the library alone won't rebuild the timeline. |
| Postgres won't start / dump errors after a power loss or SD-card host | Postgres logs; storage medium under DB_DATA_LOCATION | Postgres corruption (network share / SD card / unclean shutdown) | Per Immich's FAQ, force a dump with zero_damaged_pages=on, then restore a pre-corruption backup. |
Commands and settings paths
Back up the Immich database
docker exec -t immich_postgres pg_dump --clean --if-exists --dbname=immich --username=postgres | gzip > /backup/immich-dump.sql.gz
Where: On the Docker host running the Immich stack
Expected: A non-empty gzipped SQL dump is written.
Failure means: An error or near-empty file means the container name/DB creds are wrong, or the DB is unhealthy.
Safe next step: Confirm DB_DATABASE_NAME/DB_USERNAME (default immich/postgres) and that immich_postgres is healthy.
Confirm which asset folders to back up
echo $UPLOAD_LOCATION # back up its library/, upload/, profile/ subfolders
Where: On the Docker host (read your .env)
Expected: UPLOAD_LOCATION resolves to your data path; library/upload/profile hold the originals.
Failure means: If empty, the variable isn't set in .env and the default path is in use.
Safe next step: Back up library/, upload/, profile/ only; skip regenerable thumbs/encoded-video.
Restore the database into a clean stack
gunzip < /backup/immich-dump.sql.gz | docker exec -i immich_postgres psql --dbname=immich --username=postgres --single-transaction --set ON_ERROR_STOP=on
Where: On the new host after docker compose create + starting immich_postgres
Expected: psql applies the dump with no ON_ERROR_STOP abort.
Failure means: An 'earth type does not exist' error is the known reverse-geocoding restore bug.
Safe next step: Apply Immich's documented sed fix to the search_path line before piping into psql.
Hardware and platform boundary
Change only when
- Add a scheduled DB-dump sidecar once you have more than a trivial library — manual dumps get skipped.
- Move DB_DATA_LOCATION onto reliable local storage (not an SD card or network share) if you've seen Postgres corruption.
Evidence that matters
- A consistent DB-dump + library/upload/profile backup pair.
- Reliable local storage for Postgres and a UPS-clean shutdown.
- Matching app/server versions on the same v2.x line.
Evidence that does not matter
- Backing up thumbnails or encoded video — they regenerate.
- Chasing a faster phone uploader when the real fix is an OS background-task setting.
Avoid
- Restoring the library without its matching database dump.
- Relying on experimental self-signed-cert/Basic-Auth connection features for upload — they often break it.
Related tool
Use the linked tool to turn this runbook into a guided check for your exact setup.
Backup plan builderRelated problems
Last reviewed
2026-06-03 · Reviewed by HomeTechOps. Built from 2026-06 research verified against Immich's backup-and-restore docs, the backup FAQ, and the stable-release notes. The operator differentiator is separating the (common) mobile-OS background-task cause from the (dangerous) DB/library backup-consistency cause, and insisting the restore is proven on a scratch stack rather than assumed.
Sources/assumptions
- Assumes the official Immich Docker Compose stack (immich-server, machine-learning, Redis, and the custom ghcr.io/immich-app/postgres image) with the mobile app installed.
- Immich reached stable v2.0.0 (2025-10-09) and uses semantic versioning; the current line is v2.7.x as of 2026-06. v3.0.0 was in release-candidate state, not confirmed final — verify the GitHub releases page before assuming it shipped.
- Backup commands are stated from Immich's own backup-and-restore docs (pg_dump of the immich DB + the original-asset folders); thumbnails/encoded video are regenerable and not backed up.
Source-backed checks
HomeTechOps turns official docs and conservative safety rules into a shorter runbook. These links are the source trail for the page direction.