Free up iCloud storage without losing a single photo.
iCloud Offload moves your iCloud Photos library to your own computer, in a way that is designed to be boring and safe:
- Download everything created on or before a cutoff date you choose.
- Verify that every one of those items really exists on disk (filename and exact byte size, checked against iCloud).
- Only then delete them from iCloud. Deleted items land in iCloud's Recently Deleted album, where they stay recoverable for about 30 days.
Move the cutoff forward whenever you like and repeat. Photos newer than the cutoff stay in iCloud and on your phone, untouched.
It is a friendly terminal app around the excellent icloudpd, which does the actual talking to Apple. Works on Linux, macOS and Windows.
Warning: the whole point of this tool is to delete photos from iCloud. It tries very hard to only do so after proving the local copy is complete, but you are responsible for your own backups. Read the safety section before you use the offload step, and keep a second copy of the backup folder somewhere.
Linux / macOS (Terminal):
curl -fsSL https://raw.githubusercontent.com/hacktisch/icloud-offload/main/install.sh | bashWindows (PowerShell):
irm https://raw.githubusercontent.com/hacktisch/icloud-offload/main/install.ps1 | iexThe installer makes sure Python 3.10+ and pipx are
present (installing them with your system's package manager / winget if
not), then installs icloud-offload together with icloudpd into an isolated
environment. Nothing else on your system is touched.
Already have pipx? Then simply:
pipx install https://github.com/hacktisch/icloud-offload/archive/refs/heads/main.tar.gzTo uninstall: pipx uninstall icloud-offload.
icloud-offloadThe first run walks you through setup: your Apple ID, where to store the backup (default: a folder called iCloud Offload in your Pictures folder), and the cutoff date. Then you get a menu:
1 Set cutoff date everything on or before this date is in scope
2 Preview download list what would be downloaded, changes nothing
3 Download back up everything up to the cutoff
4 Verify confirm every item up to the cutoff is on disk
5 Preview deletion count what would be removed from iCloud, changes nothing
6 Offload download + verify + DELETE from iCloud
7 Test on one album the full offload flow, scoped to a small album
8 Settings Apple ID, backup folder, folder layout
q Quit
A sensible first session:
- Pick a cutoff a year or two back, so the first batch is manageable.
- Download (option 3). The first time, iCloud asks you to sign in and enter a two-factor code right there in the terminal. Downloads are resumable: run it again and it skips what it already has.
- Test on one album (option 7): make a small album in the Photos app with two old photos, and run the whole download-verify-delete flow on just that album. It takes seconds and shows you exactly what will happen to the full library.
- Offload (option 6). Confirm by typing
delete. Go make coffee. - Check your phone: the old photos are gone from iCloud, still on disk, and in Recently Deleted for 30 days just in case.
Later, move the cutoff forward and repeat.
Everything in the menu is also a sub-command, for scripts and cron jobs:
icloud-offload status
icloud-offload set-cutoff 2024-01-01
icloud-offload download [--dry-run] [--album NAME]
icloud-offload verify [--album NAME]
icloud-offload preview-delete
icloud-offload offload [--album NAME] [--yes]
icloud-offload albums
icloud-offload paths # where the config file and logs liveEvery icloudpd run is logged in full to the logs folder shown by
icloud-offload paths.
The backup folder holds your originals in YYYY/MM/ sub-folders (HEIC, JPEG,
MOV, Live Photo pairs...). Any photo manager can index it. If you want the
Apple-Photos-like experience (faces, map, semantic search) on your own
machine, Immich pointed at the backup folder as an
external library works very well. Mount the backup folder read-only in
Immich: those files are the only copy you have left.
- Verify before delete, always. The offload step first asks iCloud to list every item up to the cutoff that is not on disk (icloudpd matches on filename plus exact byte size). Anything missing is downloaded and the check repeats. Deletion only starts once that list is empty. If a run fails or the list is not empty, nothing is deleted.
- Two independent bounds on what gets deleted. The delete run tells icloudpd both "skip anything created after the cutoff" and "keep anything more recent than N days", where N is the age of the cutoff. Either alone would already protect everything newer than the cutoff.
- Nothing recent. The cutoff must be at least one full day in the past.
- Recently Deleted. Deleting through the iCloud API is the same as deleting in the Photos app: items go to Recently Deleted and can be restored from any device for about 30 days.
- Explicit confirmation. The offload step shows exactly what it is about
to do and requires you to type
delete. - Dry runs for everything. Preview download and preview deletion change nothing, and a scoped test on a throwaway album exercises the real flow on two photos.
- Full logs. Every icloudpd invocation, with its full output, is saved to a timestamped log file.
What it does not protect against: a broken disk holding the only copy. Keep a second backup of the backup folder.
- Two-factor authentication prompts come from icloudpd and appear in
the terminal. The session is cached for a while (in icloudpd's cookie
folder,
~/.pyicloudby default), so you are not asked every run. - Advanced Data Protection (end-to-end encrypted iCloud) is not supported by icloudpd, and therefore not by this tool. Apple also requires "Access iCloud Data on the Web" to be enabled in iCloud settings.
- Runs are slow on big libraries. Apple's API only lists a library newest-first, with no way to stop early, so every step walks the whole library (about one request per 100 items). Verify on a 50,000-item library takes a few minutes; the download of course depends on how much there is. The tool is careful to walk the library as few times as possible.
- Endless deletion runs. Apple's API has a bug where the last page of a listing is served forever. The tool detects this, stops the run, and continues correctly. See docs/HOW_IT_WORKS.md for this and the other icloudpd quirks it works around.
- Folder layout defaults to
YYYY/MM; change it under Settings (any icloudpd--folder-structurevalue works). Don't change it after the first download, or verification will report everything as missing. - Config file:
icloud-offload pathsshows where it is. It is plain JSON;ICLOUD_OFFLOAD_CONFIGor--configpoint the tool at a different one (handy for two Apple IDs).
- "icloudpd was not found": reinstall with
pipx reinstall icloud-offload. - Verify says files are missing but they are on disk: the filename or the size differs from what iCloud has. Usually the folder layout changed, or a download was interrupted mid-file. Run Download again; it re-fetches mismatched files.
- "passes stopped making progress": iCloud is lagging behind its own deletions. Wait a bit and run the offload again.
- Stuck at sign-in / 2FA: run
icloudpd --username you@example.com --auth-onlyonce by hand to refresh the session, then try again. - Anything else: look at the newest file in the logs folder and open an issue with the relevant lines.
git clone https://github.com/hacktisch/icloud-offload
cd icloud-offload
python3 -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest
ruff check .The tests never talk to iCloud: they run the workflow against a scripted
fake icloudpd (tests/fake_icloudpd.py). See
CONTRIBUTING.md.
MIT. Not affiliated with Apple. Built on icloudpd, thank you to its maintainers.