Skip to main content
Builder’s Journal

How to Move Your iCloud Photos to Immich With icloudpd

· · 4 min read

How to Move Your iCloud Photos to Immich With icloudpd

To get your photos out of iCloud and into a self-hosted Immich library, you don’t need Apple’s export tool or a browser — you pull the originals straight down with icloudpd, organised by date, then import them into Immich. The two things that make it painless instead of miserable: an incremental flag so re-runs don’t re-scan your entire library, and a separate auth-only step to get past Apple’s two-factor prompt without downloading anything.

What you’ll learn: the exact icloudpd command I use, the flags that matter, how to handle Apple 2FA, and how the downloaded files feed into Immich.

Evidence note: this is the real command from my own migration; the download landed on my NAS and was imported into Immich. My Apple ID is replaced with a placeholder.

The one command

icloudpd 
  --username YOUR_APPLE_ID 
  --directory /path/to/icloud-staging 
  --folder-structure "{:%Y/%Y-%m}" 
  --size original 
  --no-progress-bar 
  --until-found 100

That downloads every photo and video from iCloud to /path/to/icloud-staging, sorted into YYYY/YYYY-MM folders, at full original quality. Run it again next month and it picks up only what’s new. Here’s why each flag earns its place.

--size original

Download the full-resolution originals, not iCloud’s optimised/downscaled copies. This is the whole point of getting your photos out — you want the real files, HEIC/Live Photos and all. It costs space, so point --directory at somewhere roomy (mine is a NAS share).

--folder-structure "{:%Y/%Y-%m}"

Lay the files out by year and month (2024/2024-08/…) instead of one giant flat folder. This makes the staging area browsable, makes an import into Immich’s date-based structure clean, and keeps any filesystem happy (no directory with 60,000 files in it).

--until-found 100 — the incremental flag

This is the flag that turns a 6-hour re-scan into a 30-second one. icloudpd walks your library newest-first; --until-found 100 tells it to stop after it hits 100 photos it has already downloaded. On the first run it downloads everything; on every run after, it grabs the new photos at the top and bails once it’s clearly into already-synced territory. Without it, each run re-checks your entire library. (Pick a number comfortably larger than a typical batch — 100 is plenty.)

--no-progress-bar

Cleaner logs when you’re running it unattended or from a script.

Getting past Apple’s two-factor

Apple requires 2FA, and that trips people up. Two things to know:

  • Do the auth as its own step. Before a big download, authenticate on its own so you can type the 2FA code without a half-finished sync hanging on it:

bash icloudpd --username YOUR_APPLE_ID --directory /path/to/icloud-staging --auth-only

Enter the code Apple sends; icloudpd stores a session cookie (under ~/.pyicloud) so subsequent runs are non-interactive — until the session expires.

  • Sessions expire. Every so often Apple invalidates the trusted session and you’ll need to re-run the --auth-only step and re-enter a code. That’s the one part you can’t fully automate — plan for an occasional manual re-auth rather than a truly hands-off cron.

Feeding it into Immich

Once the originals are on disk in /path/to/icloud-staging, you import them into Immich one of two ways:

  • External library — point Immich at the staging folder as an external library so it indexes the files in place (good if you want to keep the folder structure as the source of truth).
  • Upload + remove — use the Immich CLI to upload the staging folder into Immich’s managed library, then delete the staging copy once you’ve confirmed it’s all in.

Either way, Immich’s duplicate detection means a re-run that re-downloads a handful of overlapping photos won’t create duplicate assets — it recognises them on import.

Caveats worth knowing

  • Space. Originals are big. Stage them somewhere with room to spare, not your boot disk.
  • Live Photos / HEIC. icloudpd pulls the originals including the paired video for Live Photos; Immich handles HEIC, but check a few imported items look right before you delete the staging copies.
  • Not fully hands-off. The 2FA re-auth means this is "incremental with an occasional manual login," not "set-and-forget."
  • Keep the staging until you’ve verified. Don’t delete the downloaded files until Immich shows the imported count you expect.

A reusable checklist

  1. Authenticate first: icloudpd --username … --auth-only, enter the 2FA code.
  2. Download originals by date: --size original --folder-structure "{:%Y/%Y-%m}".
  3. Make re-runs incremental with --until-found 100.
  4. Import the staging folder into Immich (external library, or CLI upload).
  5. Verify the count in Immich before deleting the staging copies.
  6. Expect an occasional manual re-auth when Apple expires the session.

Takeaway

icloudpd is the clean way off iCloud: it pulls your real originals, sorts them by date, and — with --until-found — syncs incrementally so you can re-run it without re-scanning everything. The only genuinely manual part is Apple’s 2FA, so do the --auth-only step deliberately and don’t expect a fully unattended job. After that, the staged folder drops straight into Immich.

AI assisted with drafting; the command, flags, and workflow are from my own iCloud-to-Immich migration.

Share this article

Leave a Reply

Your email address will not be published. Required fields are marked *