immich-export
Overview
Export supported originals and metadata from Immich — albums, people, tags, descriptions, favorites, and coordinates — into a human-readable local folder tree.
The result is useful for inspection, migration, and an additional local copy. It is not a replacement for independent, tested backups of Immich and its database.
immich-export/
library/2024/03/IMG_1234.jpg # primary tree (self-contained mode)
library/2024/03/IMG_1234.jpg.xmp # sidecar: tags, people, albums, description, geo, favorite
albums/Japan-2019/IMG_1234.jpg # → symlink into library/
people/Anna/IMG_1234.jpg # → symlink into library/
manifest.jsonl # append-only verified-state history
manifest-current.jsonl # authoritative current verified set
manifest.csv # human-readable current projection
export-report.txt # counts, warnings, errors, timing
Status
The latest verified public release is 1.0.0 across GitHub Releases, PyPI, and the Fileworks Homebrew tap.
Install
pipx install immich-export
# or
brew install fileworks/tap/immich-export
Released 1.0.0 on
PyPI,
GitHub, and
fileworks/tap (verified 2026-08-12).
Quick start
export IMMICH_SERVER=https://immich.local:2283
export IMMICH_API_KEY=… # never passed on the command line
immich-export --out ~/immich-export
The first run copies originals and writes sidecars. Every later run rehashes what is already there and transfers only what changed.
Usage
export IMMICH_SERVER=https://immich.local:2283
export IMMICH_API_KEY=... # Immich → Account Settings → API Keys
# full portable export (copies originals)
immich-export --out ./immich-export
# verified re-run: local originals are rehashed; only missing/changed bytes download
immich-export --out ./immich-export
# sidecar mode: you already have the Storage-Template tree mounted —
# only write .xmp sidecars + album/people views next to it
immich-export --mode sidecar --library-root /volume1/photos --out /volume1/photos
# custom primary tree
immich-export --layout "{year}/{album}" --out ./export
Key flags (see immich-export --help for all):
| Flag | Default | Meaning |
|---|---|---|
--mode |
self-contained |
self-contained copies originals; sidecar only writes XMP + views next to an existing tree |
--layout |
{year}/{month} |
primary tree; tokens {year} {month} {day} {album} {type}; {album} falls back to Unsorted |
--album-view / --people-view |
on | build albums/ and people/ symlink views |
--sidecars |
xmp |
xmp or none |
--since |
— | only assets taken on/after this date |
--resume |
on | use prior state as a resume/migration hint; local bytes are still rehashed |
--include-hidden |
off | also export hidden + locked-folder assets |
--stale-assets |
keep |
keep/report absent outputs or explicitly move owned outputs to quarantine |
--concurrency |
4 |
bound concurrent API work, downloads, and local verification |
--manifest-batch-size |
128 |
verified history records per durable synchronization |
--manifest-flush-interval |
0.1 |
maximum seconds before a partial durable group is synchronized |
--history-max-records |
100000 |
rotate the bounded active history at this record count |
--history-max-bytes |
134217728 |
rotate the active history at this byte count |
--log-file |
<out>/immich-export.log |
bounded rotating timestamped diagnostics |
Verified behavior
- Read-only against Immich. Never writes back.
- Verified originals. Downloads are SHA-1 checked before atomic promotion, and existing local originals are rehashed on every run in both modes.
- Canonical metadata and XMP. All persisted/path/XMP fields share one typed state. Missing, malformed, or stale required XMP is atomically refreshed.
- History versus current.
manifest.jsonlis bounded active audit history; digest-linked verified archives live undermanifest-history/.manifest-current.jsonl, its CSV projection, and generated views contain only assets verified by the latest completed compatible scan. - Partial runs are explicit. Asset-specific integrity failures are reported,
excluded from current state, and return exit code
1(PARTIAL); run-level failures do not replace the prior current snapshot. - Conservative reconciliation. A compatible full scan removes absent assets from current state and views. Their files remain reported orphans by default. Explicit quarantine moves only manifest-owned outputs; sidecar mode never moves Immich-managed originals.
- Bounded work and visible progress. Assets are paged, concurrent work is bounded, and terminal/log progress remains available.
These checks cover the API fields and files the exporter supports. They cannot detect metadata Immich does not expose, storage failures that occur after a successful verification, or prove that an export can restore an entire Immich installation. Keep separate backups and test restoration procedures.
Exit codes
immich-export, paperless-export and unpacksort share one exit-code
vocabulary, so a script can branch on the code without knowing which tool it
ran. The class of outcome is the same everywhere; the specific condition is
this tool's, and the table below is what it means here.
| Code | Name | Meaning |
|---|---|---|
| 0 | SUCCESS |
everything asked for was done |
| 1 | PARTIAL |
the run finished with one or more asset failures; successes are published |
| 2 | USAGE |
bad flags, paths, or credentials the server rejected — nothing was attempted |
| 3 | CONFLICT |
the Immich server is unreachable |
| 4 | FATAL |
unexpected failure, or output that could not be written (re-run with --verbose) |
| 130 | INTERRUPTED |
cancelled by the operator |
Sidecar format
Standard XMP wherever a standard slot exists — dc:subject (tags),
Iptc4xmpExt:PersonInImage (people), dc:description, photoshop:DateCreated,
exif:GPSLatitude/Longitude, xmp:Rating (favorite → 5) — so digiKam,
Lightroom and exiftool can read them. Album membership and Immich ids live in a
custom immich: namespace in the same file.
When XMP is enabled, an asset is current only after its canonical sidecar matches the manifest state. Exact generated-state validation removes metadata that was deleted in Immich rather than retaining stale XMP nodes.
Immich API compatibility
Built against the Immich v3 API (spec version 3.0.1). Instead of a
generated client, the exact API slice used is declared in
src/immich_export/api_contract.py and checked in CI against a vendored,
pruned copy of the official OpenAPI spec. To check a new Immich release:
uv run python scripts/refresh_api_spec.py --ref v3.1.0
uv run pytest tests/test_contract.py
A removed endpoint or field fails the tests before it breaks at runtime.
Configuration
Everything is configured through environment variables and flags; there is no configuration file to keep in sync.
| Variable | Purpose |
|---|---|
IMMICH_SERVER |
Base URL of your Immich instance |
IMMICH_API_KEY |
API key. Read from the environment only, never from argv |
IMMICH_EXPORT_CONCURRENCY |
Parallel downloads (default: conservative) |
IMMICH_EXPORT_MANIFEST_BATCH_SIZE |
Records per durable history group |
IMMICH_EXPORT_MANIFEST_FLUSH_INTERVAL |
Maximum partial-group delay in seconds |
IMMICH_EXPORT_HISTORY_MAX_RECORDS |
Active-history record rotation threshold |
IMMICH_EXPORT_HISTORY_MAX_BYTES |
Active-history byte rotation threshold |
IMMICH_EXPORT_LOG_FILE |
Rotating logfile path |
Flags are documented under Usage; --help is authoritative.
Troubleshooting
The run stops with a network error. Re-run it. The manifest records what was verified, so a repeat run resumes rather than restarting.
A previous run was interrupted and the manifest looks damaged. Damaged lines are skipped and reported; the export continues from the last intact state.
Nothing is downloaded and the count is zero. Check that IMMICH_SERVER
points at the API root and that the key has library access — a wrong base URL
authenticates fine and returns nothing.
Symlinks fail on the target. Album and people views require symlinks.
Disable them with --no-album-view --no-people-view on FAT/exFAT or cloud
folders that cannot represent links; the verified primary export remains
available.
Development
uv sync --locked --all-extras --all-groups
uv run ruff check . && uv run ruff format --check . # lint
uv run mypy # strict types
uv run pytest # tests (mock Immich API)
uv build # sdist + wheel
Pull requests run linting, strict types, tests, and builds on Python 3.12, 3.13, and 3.14, plus documentation links, dependency auditing, and an installed-wheel smoke test. Renovate batches routine dependency changes into one Monday PR and automerge is enabled; a per-repository concurrency limit of one prevents stacked dependency branches or PRs.
A separate Sunday workflow runs the real disk-backed reconcile and publication
budget at 500,000 assets, keeping schedule-only work out of pull-request and
main CI checks.
Conventional Commits drive python-semantic-release. After CI passes on a
main push, the release workflow pins the tested SHA, verifies the staged
source, tag, distributions, and installed CLI, then atomically pushes the
release commit and tag. Publication proceeds through the protected
github-release, pypi, and homebrew environments in that order; PyPI uses
trusted publishing and GitHub Releases carry the verified wheel and sdist.
For per-clone paths, commands, or preferences, create an ignored
CLAUDE.local.md at the repository root. Do not put credentials or other
secrets in it.
Security
Report vulnerabilities privately as described in SECURITY.md. The API key is read from the environment and never written to disk, a log line, or the manifest.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file immich_export-1.0.1.tar.gz.
File metadata
- Download URL: immich_export-1.0.1.tar.gz
- Upload date:
- Size: 165.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce089222d6d58fd666482212838ed689ffe6d83c49ad9f75aa2af8969c4dc28b
|
|
| MD5 |
f341521d67915841ae54b158862dd4be
|
|
| BLAKE2b-256 |
ba4f53717eca9544acbe5da3f460bb29f16c052909c55c3e6d519ef35f1f5f7b
|
Provenance
The following attestation bundles were made for immich_export-1.0.1.tar.gz:
Publisher:
release.yml on fileworks/immich-export
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
immich_export-1.0.1.tar.gz -
Subject digest:
ce089222d6d58fd666482212838ed689ffe6d83c49ad9f75aa2af8969c4dc28b - Sigstore transparency entry: 2446084150
- Sigstore integration time:
-
Permalink:
fileworks/immich-export@c8464777c651cf3332e92e4f31728b5cdae4647d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/fileworks
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c8464777c651cf3332e92e4f31728b5cdae4647d -
Trigger Event:
workflow_run
-
Statement type:
File details
Details for the file immich_export-1.0.1-py3-none-any.whl.
File metadata
- Download URL: immich_export-1.0.1-py3-none-any.whl
- Upload date:
- Size: 46.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
afba770321c96e4710482ba299cfb75850128f191924a3c719187a800017d887
|
|
| MD5 |
df75c3c833ee0c2f0746cc51fbfd8c17
|
|
| BLAKE2b-256 |
a8fe6b1387577e75c40ef14f3105dea9c7f8bfaadd7cdf48a0e7ec225c8eadc8
|
Provenance
The following attestation bundles were made for immich_export-1.0.1-py3-none-any.whl:
Publisher:
release.yml on fileworks/immich-export
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
immich_export-1.0.1-py3-none-any.whl -
Subject digest:
afba770321c96e4710482ba299cfb75850128f191924a3c719187a800017d887 - Sigstore transparency entry: 2446084362
- Sigstore integration time:
-
Permalink:
fileworks/immich-export@c8464777c651cf3332e92e4f31728b5cdae4647d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/fileworks
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c8464777c651cf3332e92e4f31728b5cdae4647d -
Trigger Event:
workflow_run
-
Statement type: