Mount iCloud Drive on Linux as a fast local-first FUSE filesystem with persistent caching, selective hydration, and on-demand sync.
icloud-linux makes your iCloud Drive show up like a normal folder on Linux.
It is designed to feel much more local than a naive network mount:
- folders and filenames are cached on disk
- file contents are downloaded into a local mirror on demand or in the background
- reads usually come from local storage, not from iCloud on every access
- local changes are written immediately and synced back on the next sync pass
- remote changes are pulled in by the sync engine on demand or on a timer
In practice, that means find, editors, shells, and normal file browsing work against a persistent local cache instead of blocking on iCloud for every operation.
There are three main pieces:
- Metadata crawl: the first run scans your iCloud Drive and builds a local index.
- Hydration: file contents are downloaded into the local cache — either on demand when a file is opened (lazy mode) or proactively in the background (background mode).
- Sync engine: local edits upload and remote changes are refreshed either automatically on a timer or on demand via
icloudctl sync.
Important behavior:
- The mount is local-first.
- Restarts reuse the existing cache instead of starting from zero.
- If you open a file before it has finished hydrating, that file is downloaded first and then served locally.
- Failed warmup downloads are retried automatically with backoff.
- If a path changed both locally and remotely, the local version is preserved as a conflict copy instead of being silently overwritten.
- With
auto_sync: false, the driver starts and mounts immediately without spawning background polling threads. Runicloudctl syncwhen you want a fresh pull from iCloud.
This project is for people who want:
- a normal folder they can browse on Linux
- Apple ID + 2FA support
- a persistent local cache
- control over which folders are hydrated vs stub-only
- on-demand syncing instead of constant background polling
If you want a quick setup and do not care about the internal details, use ./icloudctl quickstart.
You need:
- Linux
- Python 3 with
venv - FUSE
systemctl --user
sudo apt-get update
sudo apt-get install -y fuse libfuse-dev pkg-config python3-venvsudo dnf install python3-devel fuse fuse-libs fuse-devel gcc makegit clone https://github.com/IsmaeelAkram/icloud-linux.git
cd icloud-linux
./icloudctl quickstart ~/iCloudThis will:
- create the Python virtual environment
- install dependencies
- create the config and user service
- ask for your Apple ID email and password
- run the one-time interactive authentication flow
- start the background service
After setup, your files will be mounted at ~/iCloud unless you chose another path.
If you prefer to do setup one step at a time:
./icloudctl init ~/iCloud
./icloudctl configure
./icloudctl auth
./icloudctl startSystemd user services are non-interactive. They cannot pause and wait for a 2FA code.
So this project uses:
./icloudctl authfor the interactive one-time Apple login and 2FA flow- a generated user service that reuses the saved session cookies in the background
If Apple expires your session, run:
./icloudctl auth
./icloudctl restartiOS 26 beta may deliver a push notification popup instead of a numeric 2FA code. If this happens, use the --force-sms flag to bypass the push path and request an SMS code directly:
./icloudctl auth --force-smsUse --debug to see Apple's reported auth mode and diagnose delivery issues:
./icloudctl auth --debug./icloudctl start
./icloudctl stop
./icloudctl restart
./icloudctl refresh
./icloudctl status
./icloudctl logs
./icloudctl doctor
./icloudctl hydrate [--dry-run] [--verbose]
./icloudctl sync [--timeout SECONDS] [--quiet]
./icloudctl clear-cache
./icloudctl uninstallWhat they do:
start: starts the background user servicestop: stops the service and unmounts the folderrestart: restarts the service cleanlyrefresh: asks the running service to crawl remote iCloud Drive metadata nowstatus: shows whether the service is runninglogs: tails the service logsdoctor: checks common setup issueshydrate: blocks until all eligible files (persync_paths/exclude_paths) are fully downloaded locally. Use this before copying files to ensure nothing triggers a mid-copy download.--dry-runshows what would be downloaded without actually downloading.sync: triggers an on-demand remote metadata sync in the running driver (sends SIGUSR1, waits for completion). Useful whenauto_sync: falseis set.clear-cache: deletes the local mirror and sync database, then rebuilds them on next startuninstall: removes the generated user service
On the first run:
- the service crawls your iCloud Drive metadata
- it mounts the folder
- if
warmup_mode: background, it starts downloading file contents into the local cache - if
warmup_mode: lazy, file contents download only when each file is first opened
On later runs:
- it reuses the cache stored on disk
- if
auto_sync: true, it refreshes remote metadata and uploads local changes on a timer - if
auto_sync: false, it mounts immediately with no background polling; runicloudctl syncon demand
By default all of iCloud Drive is indexed (stubs created everywhere), but you can restrict which paths have their file contents downloaded.
sync_paths — allow-list. Only paths in this list will have file contents downloaded. Everything else is stubs only.
exclude_paths — deny-list. Paths matching these prefixes are never hydrated, even if they fall under a sync_path. The deny-list is evaluated first.
Example config:
# Only hydrate /Downloads
sync_paths:
- /Downloads
# But skip this large subfolder for now
exclude_paths:
- /Downloads/Large ArchiveWith this config:
/Downloads/*→ file contents downloaded/Downloads/Large Archive/*→ stubs only, no download- Everything else on iCloud → stubs only
When you're ready to hydrate an excluded folder, remove it from exclude_paths and restart the service.
auto_sync: true (default) — background threads poll iCloud on the configured upload_interval_seconds and remote_refresh_interval_seconds schedules. Good for setups where files change frequently.
auto_sync: false — no polling threads start. The driver mounts and waits. Use icloudctl sync to pull the latest remote changes when you need them. Good for low-churn libraries where constant polling is wasteful and you want predictable resource usage.
When auto_sync: false, the recommended workflow is:
icloudctl sync # pull latest metadata from iCloud
icloudctl hydrate # download file contents for all eligible paths
# now copy from mirror: ~/.cache/icloud-linux/mirror/Downloads/Once files are hydrated, copy from the local mirror rather than from the FUSE mount:
cp -r ~/.cache/icloud-linux/mirror/Downloads/ ~/Desktop/intake/The mirror is a plain directory on disk — reads are instant, no network involved. Copying from the FUSE mount works too but may trigger downloads for any files not yet hydrated.
The project keeps its local state here:
- Config:
~/.config/icloud-linux/config.yaml - Session cookies:
~/.config/icloud-linux/cookies - Service env:
~/.config/icloud-linux/icloud.env - User service:
~/.config/systemd/user/icloud.service - Local cache root:
~/.cache/icloud-linux - Local mirror:
~/.cache/icloud-linux/mirror - Sync state database:
~/.cache/icloud-linux/state.sqlite3 - Logs:
~/.local/state/icloud-linux/icloud.log - On-demand sync marker:
~/.local/state/icloud-linux/sync_done
This repo is not just a read-only mount and it is not only a foreground downloader.
The sync engine:
- tracks local dirty files and directories
- uploads local changes (when
auto_sync: trueor onicloudctl sync) - refreshes remote metadata (when
auto_sync: trueor onicloudctl sync) - hydrates missing file contents on demand or in the background
- preserves local conflict copies when local and remote diverge
That makes it closer to a real cached sync client than a simple network filesystem wrapper.
Run:
./icloudctl status
./icloudctl doctor
./icloudctl logsRun:
./icloudctl auth
./icloudctl restartWhen running under systemd (no TTY), a failed auth parks the service in unauthenticated mode instead of crashing — this prevents the service from crash-looping and triggering Apple's account lockout. You will see UNAUTHENTICATED mode in the logs. Fix by running:
./icloudctl auth
./icloudctl restartRun:
./icloudctl clear-cacheThat removes the local mirror and sync database. The next start will rebuild them from iCloud.
After the service has had time to hydrate files:
find ~/iCloud -type f | headYou can watch logs in another terminal:
./icloudctl logsNormal activity with auto_sync: false will show the reconcile pass at startup and then go quiet until you run icloudctl sync.
./icloudctl hydrate --dry-runThis reports how many files are already local vs need downloading, without downloading anything.
- Warmup downloads are intentionally conservative because iCloud file downloads are sensitive to aggressive parallelism.
- The generated systemd unit is created by
./icloudctl; the repo does not rely on checked-in service files anymore. - This project currently targets a user-level systemd service, not a system-wide root service.
- When
auto_sync: false, the SIGUSR1 signal triggers a one-shot sync in the background;icloudctl synchandles sending that signal and waiting for completion.