Skip to content

☁️ Cloud Sync

Declarative, multi-remote file synchronization using rclone and pueue. Pin directories with .cloud-pin files to control how they sync.

Config User Config · Sample cloud-sync.toml
CLI CLI Tools · Functions · Hooks
Architecture Architecture

In this doc: Quick Start · Concepts · Commands · Configuration · rclone Discovery · Pin Files · Troubleshooting


Quick Start

# 1. Copy the sample config
cp $ZSHAND/samples/user-config/cloud-sync/cloud-sync.toml \
   $ZSHAND_CONFIG_DIR/cloud-sync/cloud-sync.toml

# 2. Create an env file for your remote (fast detection)
mkdir -p $ZSHAND_CONFIG_DIR/cloud-sync/env
cat > $ZSHAND_CONFIG_DIR/cloud-sync/env/gdrive.env <<EOF
REMOTE=gdrive
MOUNT_POINT=~/GoogleDrive
LOCAL_MIRROR=~/Local_Work/gdrive
EOF

# 3. Pin a directory for bisync
cd ~/GoogleDrive/Projects/myapp
pin                        # interactive — choose mirror or cache
# — or —
pin_mirror . gdrive        # non-interactive

# 4. Run a sync scan
cloudsync scan --dry-run   # preview what would happen
cloudsync scan             # queue real sync jobs via pueue

# 5. Check status
cloudsync status

# — or push (scan + wait + report in one) —
cloudsync push admsep      # sync admsep and wait for completion
cloudsync watch admsep     # same, with live progress bar
cloudsync recover          # diagnose and fix any sync issues

Concepts

Two Sync Strategies

Strategy Command Use Case
mirror rclone bisync Code, git repos, config — bidirectional sync to local ext4
cache rclone rc vfs/refresh Media, docs — pre-heat the VFS cache (rclone-mount only)

Two Remote Types

Type Config Keys Description
rclone-mount mount_point, local_mirror FUSE-mounted cloud drive (Google Drive, Dropbox, etc.)
rclone-sftp local_path, remote_path SFTP/SSH remote (NAS, server) — no VFS, mirror only

Settings Cascade

Settings resolve through 4 layers. Scalar values replace (non-empty wins). Exclude arrays merge:

  1. [defaults] — global defaults for all pins
  2. [defaults.mirror] / [defaults.cache] — strategy-specific defaults
  3. [remotes.NAME] — per-remote overrides
  4. .cloud-pin — per-directory overrides (pin wins)

Safety Net

When you cd into a FUSE-mounted cloud directory that has no .cloud-pin (no local safety net), the chpwd hook warns:

☁️  Cloud-only on gdrive (no local safety net)
   Use 'pin_mirror' for code/git or 'pin_cache' for media

This warning only appears for true cloud-only FUSE mounts — local mirrors and SFTP local paths are already safe.

Commands

cloudsync — Sync Engine CLI

Subcommand Usage Purpose
scan cloudsync scan [NAME\|all] [--dry-run] Find .cloud-pin files and queue sync jobs
push cloudsync push [NAME\|all] [--dry-run] Scan, wait for completion, and show status
watch cloudsync watch [NAME\|all] Scan with live progress bar (spinner + progress)
recover cloudsync recover Diagnose, fix, and report sync health
status cloudsync status Show sync dashboard (last sync, results, pauses)
pause cloudsync pause [DURATION] [--remote R] Pause syncing (e.g. 2h, 30m, 1d)
resume cloudsync resume [--remote R] Resume syncing
migrate cloudsync migrate [DIR] Convert legacy .mirror/.cache.cloud-pin

Pin Functions

Function Usage Purpose
pin pin [dir] Interactive pin menu (choose strategy via gum)
pin_mirror pin_mirror [dir] [remote] Create/update mirror pin
pin_cache pin_cache [dir] [remote] Create/update cache pin
unpin unpin [dir] [remote\|all] Remove pins (per-remote or all; cleans cache files)

Configuration

cloud-sync.toml

Located at $ZSHAND_CONFIG_DIR/cloud-sync/cloud-sync.toml. See the sample config for full documentation of every field.

Key sections:

Section Purpose
[defaults] Global sync defaults (exclude, conflict_resolve, max_delete, etc.)
[defaults.mirror] Mirror-specific defaults (compare, sync_direction)
[defaults.cache] Cache-specific defaults (vfs_refresh_async)
[remotes.NAME] Per-remote config (type, paths, overrides)
[queue] Pueue group names and parallelism
[schedule] Scan interval, boot delay, quiet hours
[notifications] Desktop notification settings

Env Files

Located at $ZSHAND_CONFIG_DIR/cloud-sync/env/*.env. One per remote, KEY=VALUE format:

REMOTE=gdrive
MOUNT_POINT=~/GoogleDrive
LOCAL_MIRROR=~/Local_Work/gdrive
REMOTE=nas
LOCAL_PATH=~/nas-sync

These are read by the chpwd hook and pin functions for fast detection (no TOML parsing).

rclone Binary Discovery

When multiple rclone installations exist (mise, cargo, system package manager), cloudsync may pick the wrong one — or fail to find any if the binary isn't on PATH during a pueue job or systemd timer. The discovery system solves this by searching known installation locations in a configurable priority order.

Configuration

Add an [rclone] section to cloud-sync.toml:

[rclone]
# Optional: Absolute path to rclone binary (overrides all discovery)
binary = "/custom/path/to/rclone"

# Optional: Preferred installation source when multiple exist
# Values: "auto" (default), "mise", "cargo", "system"
prefer = "mise"

# Optional: Minimum required rclone version
min_version = "1.60.0"
Key Type Default Description
binary string (none) Absolute path to rclone; skips all discovery if set and valid
prefer string "auto" Which installation source to try first: mise, cargo, system, or auto
min_version string (none) Minimum rclone version (e.g. 1.60.0); binaries below this are skipped

Environment Variable

As an alternative to the config file, set the CLOUDSYNC_RCLONE_BIN environment variable:

export CLOUDSYNC_RCLONE_BIN="$HOME/.local/bin/rclone"

This is useful for one-off overrides or CI environments where you don't want to modify the TOML config.

Discovery Priority Order

cloudsync resolves the rclone binary in this order, stopping at the first valid match:

Priority Source How
1 Config rclone::binary Absolute path from [rclone] binary in cloud-sync.toml
2 $CLOUDSYNC_RCLONE_BIN Environment variable
3 Preference-based discovery Tries misecargosystem (default auto order), or the custom order set by prefer
4 Common hardcoded paths /usr/local/bin/rclone, /opt/homebrew/bin/rclone, /usr/bin/rclone

Every candidate is validated before use — cloudsync runs rclone version to confirm the binary is real rclone, and checks min_version if configured. Invalid or too-old binaries are silently skipped.

Verifying Which rclone Is Used

During cloudsync scan, the resolved binary path is printed at the start:

ℹ  Using rclone: /home/user/.local/share/mise/installs/rclone/1.68.2/bin/rclone

Use --dry-run to check without queuing any jobs:

cloudsync scan --dry-run

If no rclone is found at any priority level, cloudsync exits with an error listing all sources it tried.

.cloud-pin Files

Placed in any synced directory. TOML format, one section per remote:

[gdrive]
strategy = "mirror"
exclude = ["*.log", "tmp/"]

[nas]
strategy = "mirror"
  • Valid strategies: mirror, cache
  • Cache is only valid for rclone-mount remotes (needs VFS)
  • Per-pin exclude arrays merge with all upstream layers
  • Per-pin scalar overrides (e.g. conflict_resolve) replace upstream values

Troubleshooting

"Not in a managed cloud folder"

The directory isn't under any configured remote's MOUNT_POINT, LOCAL_MIRROR, or LOCAL_PATH. Check your env file at $ZSHAND_CONFIG_DIR/cloud-sync/env/.

"rclone not found" / "pueue not found"

Install the missing dependency. Use --dry-run to preview without pueue.

Sync paused permanently

If disk filled up and the cache dir became read-only, the expired pause marker couldn't be deleted. The system now treats expired pauses as resumed regardless. Run cloudsync resume to clear the marker manually.

First bisync fails

The first run of a mirror pin always includes --resync to establish a baseline. If rclone bisync reports conflicts, review the --backup-dir and resolve manually.

Conflicts detected

cloudsync status shows ⚠ (N conflicts) when rclone output contained conflict messages. Desktop notifications are sent for conflicts (configurable via notifications::on_conflict). Review the pueue job log for details: pueue log <id>.

Status shows stale entries

unpin automatically cleans matching status/filter cache files. For manual cleanup, delete files in $XDG_CACHE_HOME/zshand/cloud-sync/status/.

Dependencies

Tool Required Purpose
rclone Yes Sync engine (bisync, sync, copy, rc)
pueue For non-dry-run Job queue with group isolation
gum Optional Interactive pin menu
Document Purpose
📟 CLI Tools All bin/ scripts
🛠️ Functions Shell functions
🪝 Hooks Startup and chpwd hooks
📂 Config User configuration