I have a 200 GB zip of our wedding photos and video. My MacBook has a 256 GB drive, so the file technically fits, but there is nowhere near 200 GB free to also drag a copy into iCloud Drive the normal way. My wife did not want to pay for more Google Drive storage, and I do not trust a single external SSD as the only home for irreplaceable files.
iCloud already had the space. The problem: macOS makes you land the whole file on local disk before it will sync, and I did not have the room.
chunkyy is the way around that. It moves the file into iCloud one chunk at a time and evicts each chunk after it uploads, so it never needs more than about one chunk of free space. The wedding zip went up from a 256 GB laptop, and I can pull it back byte-for-byte whenever I want it.
iCloud has no public "stream bytes to the server" API. The only way in is to write a file into iCloud Drive and let the sync daemon upload it, which normally needs the whole file to fit locally first. chunkyy sidesteps that wall with chunk + evict:
flowchart LR
subgraph Upload["upload (peak local: ~1 chunk)"]
direction TB
S[("source file<br/>bigger than free disk")] -->|slice range| U1[write chunk<br/>to iCloud]
U1 --> U2{iCloud reports<br/>uploaded?}
U2 -->|no, wait| U2
U2 -->|yes| U3[evict local copy<br/>frees disk]
U3 -->|next range| U1
U3 -.->|placeholder stays<br/>in iCloud| C[("chunks + manifest<br/>in iCloud")]
end
subgraph Restore["restore (peak local: ~1 chunk)"]
direction TB
C2[("chunks + manifest<br/>in iCloud")] -->|next chunk| R1[download chunk]
R1 --> R2[verify SHA-256]
R2 --> R3[append to output]
R3 --> R4[evict chunk<br/>frees disk]
R4 -->|more chunks| R1
R4 -->|done| O[("output file<br/>byte-identical")]
end
- One chunk at a time. Slice a chunk into iCloud, wait for it to finish uploading, evict the local copy, repeat. Peak local disk use is about one chunk.
- The source is never modified. chunkyy only ever reads it.
- Restore is the mirror. Download one chunk, verify its SHA-256, append it to the output, evict it, next, so restoring a 200 GB file never needs 200 GB of iCloud data local at once either.
- Resumable. Rerun an interrupted upload and it skips chunks already uploaded and evicted.
- Verified. A per-chunk SHA-256 in the manifest is checked on every restore, and the final assembled size is confirmed.
- macOS 13 (Ventura) or later, signed in to iCloud with iCloud Drive on.
- Optimize Mac Storage ON (System Settings > [your name] > iCloud). This is
what lets eviction actually free space. Run
chunkyy doctorto verify. - Enough iCloud storage quota for the whole file.
brew tap jcatama/chunkyy https://github.com/jcatama/chunkyy
brew trust jcatama/chunkyy
brew install --HEAD chunkyybrew trust is required because Homebrew treats every third-party tap as
untrusted until you vouch for it (a formula runs build commands on your Mac).
Or build from source:
swift build -c release
cp .build/release/chunkyy /usr/local/bin/ # or anywhere on your PATHVerify your Mac is ready, then upload a large file in 2 GB chunks:
chunkyy doctor
caffeinate -s chunkyy upload ~/Movies/wedding.zip 2000caffeinate -s keeps the Mac awake so uploads do not pause. The upload is
resumable: rerun the same command and it skips chunks already uploaded and
evicted.
Restore later, on any Mac signed in to the same iCloud account:
chunkyy list
chunkyy restore wedding.zip.chunks # writes ./wedding.zip
chunkyy restore wedding.zip.chunks /tmp/out.zipThe output must be on a disk with room for the full assembled file. The iCloud side stays at ~one chunk local at a time; the output file is what grows.
| Command | What it does |
|---|---|
chunkyy upload <file> [<chunk-mb>] |
Chunk a file into iCloud (default chunk size 2000 MB; file must be at least 1 GB). |
chunkyy restore <name> [<output>] |
Reassemble a bundle (output defaults to the original filename). |
chunkyy list |
Show the bundles currently in iCloud. |
chunkyy history [N] |
Print a local log of past uploads and restores. |
chunkyy doctor |
Verify iCloud is set up and that eviction frees space. |
chunkyy selftest |
Assert a byte-identical round-trip offline, no iCloud needed. |
chunkyy --help |
Show usage for every command (also -h, help). |
| Flag | Meaning |
|---|---|
--chunk-mb N |
Set chunk size (same as the positional <chunk-mb>). |
--name NAME |
Bundle name in iCloud (default <file>.chunks). |
--no-check |
Skip the pre-upload eviction check. |
Each upload writes a manifest.json next to its chunks recording the original
filename, size, chunk size, creation date, source Mac, and a SHA-256 per chunk.
Restore uses it to name the output, verify every chunk as it lands, and confirm
the final size.
chunkyy selftestslices and reassembles random data in a temp dir and asserts the result is byte-identical. No iCloud needed, runs anywhere.chunkyy doctoruploads a small probe, evicts it, and confirms the space was actually freed, the real proof that chunk + evict works on your Mac.
Remove the tool:
brew uninstall chunkyy
brew untap jcatama/chunkyyOr, if you built from source, delete the binary you copied (e.g.
rm /usr/local/bin/chunkyy).
Uninstalling does not touch your uploaded files. To also clear the local history log:
rm -rf ~/Library/Application\ Support/chunkyyYour backups live in iCloud Drive under
~/Library/Mobile Documents/com~apple~CloudDocs/chunkyy/. Deleting that folder
permanently removes those backups from iCloud (do a chunkyy restore first
if you still want the data), so leave it alone unless that is what you intend.
MIT. Open source, use it, fork it, ship it.
