gsutil rsync -r: Mirror Directories to GCS
gsutil rsync -r makes a GCS bucket match a local directory, transferring only differences and, with -d, deleting objects not present at the source.
rsync is gsutil incremental sync: it skips unchanged files, which makes repeated cache and site deploys cheap. -r recurses, -d deletes to produce an exact mirror, and -m parallelizes.
What it does
gsutil rsync compares source and destination and copies only the differences. -r makes it recursive. -d deletes destination objects that are not in the source (exact mirror). By default it compares size and, when available, checksums.
Common usage
gsutil -m rsync -r ./dist gs://my-bucket/site
# exact mirror: delete removed files too
gsutil -m rsync -r -d ./public gs://my-bucket/www
# force checksum comparison instead of mtime/size
gsutil -m rsync -r -c ./data gs://my-bucket/dataOptions
| Flag | What it does |
|---|---|
| -r | Recurse into subdirectories |
| -d | Delete destination objects not present in the source |
| -c | Compare checksums, not just size and mtime |
| -x <pattern> | Exclude paths matching the regex |
| -n | Dry run: show actions without performing them |
In CI
Combine -m for parallelism with -d for static-site deploys so removed pages are purged. -d is destructive: confirm the direction (local source then gs:// destination) and consider -n first. Because rsync skips unchanged objects, it is far cheaper than a full cp on repeated runs.
Common errors in CI
"AccessDeniedException: 403" on a run with -d means the identity lacks storage.objects.delete; -d needs delete permission. "CommandException: Caught non-retryable exception" often wraps a 403 or a 404 BucketNotFoundException. "ServiceException: 401 Anonymous caller does not have storage.objects.list access" means credentials did not resolve.