Skip to content

aws s3 sync and cp: the CLI patterns operators actually use

The aws s3 sync and aws s3 cp patterns that hold up in production — dry runs, safe deletes, exclude/include ordering, streaming, storage classes, and the transfer tuning that actually moves the needle.

aws s3 cp copies what you point it at; aws s3 sync compares source and destination and copies only what differs — by size and last-modified time, not by content hash. Sync never deletes anything unless you pass --delete, and that flag is the one that turns a typo into an outage. The patterns below are the ones that survive contact with production: dry-run first, understand the comparison rules, order your exclude/include filters correctly, and tune concurrency before blaming the network.

What’s happening

Both commands sit on the same transfer engine in the CLI. Files above the multipart threshold (8 MB by default) are split into parts and uploaded in parallel; smaller files upload whole, several at a time, governed by max_concurrent_requests (10 by default). That engine is why a single cp of a 50 GB file saturates a link that a naive script never would — and why an interrupted big copy leaves incomplete multipart parts you keep paying for.

Sync’s comparison logic is the part people misunderstand. A file is copied when the sizes differ or when the source’s last-modified time is newer than the destination’s. Same size and older-or-equal timestamp means skipped — even if the bytes changed. There is no checksum comparison. --size-only ignores timestamps entirely (useful when a build regenerates identical files with fresh mtimes), and --exact-timestamps makes S3-to-local syncs re-download when times differ at all rather than only when the remote is newer. Full flag reference lives in the S3 documentation.

What to check first

  • Which direction and what should happen to extra files at the destination. Sync without --delete only adds and updates; with it, the destination becomes a mirror — including deletions.
  • Whether the destination is versioned. A bad --delete on a versioned bucket is recoverable; on an unversioned bucket it is not.
  • Object count. Sync lists both sides before copying; on millions of objects the listing alone takes serious time and request money.
  • Who you are. aws sts get-caller-identity before anything destructive — wrong-profile accidents are a classic.

How to verify

--dryrun prints every operation without performing it, and it works on both commands:

# Preview exactly what a deploy would change — and what --delete would remove
aws s3 sync ./dist s3://my-bucket/site/ --delete --dryrun

Confirm what actually landed:

aws s3 ls s3://my-bucket/site/ --recursive --summarize | tail -2

# Spot-check one object's metadata and storage class
aws s3api head-object --bucket my-bucket --key site/index.html

The runbook

  1. Dry-run anything with --delete, every time. The flag removes destination files missing from the source. Pointing sync at the bucket root instead of a prefix, with --delete, is how whole sites disappear:

    aws s3 sync ./dist s3://my-bucket/site/ --delete --dryrun
    aws s3 sync ./dist s3://my-bucket/site/ --delete
  2. Order exclude/include filters left to right — the last match wins. To copy only one file type, exclude everything first, then re-include:

    aws s3 sync ./media s3://my-bucket/media/ --exclude "*" --include "*.jpg"

    Reversing the order (--include first) silently copies everything.

  3. Set the storage class at upload time instead of paying for a transition later:

    aws s3 cp backup.tar.gz s3://my-bucket/backups/ --storage-class STANDARD_IA
  4. Stream through the CLI with - to skip local disk entirely. Database dumps are the everyday case:

    mysqldump --single-transaction mydb | gzip | aws s3 cp - s3://my-bucket/dumps/mydb.sql.gz

    The reverse (aws s3 cp s3://... -) pipes an object to stdout for restores.

  5. Tune the transfer engine before blaming bandwidth. On a machine with headroom, raising concurrency is usually the whole fix:

    aws configure set default.s3.max_concurrent_requests 20
    aws configure set default.s3.multipart_chunksize 64MB
  6. Use --size-only when mtimes lie. CI checkouts and build steps reset timestamps, making sync re-upload identical files; --size-only stops the churn (at the cost of missing same-size content changes).

When this is the wrong move

Sync is the wrong tool for millions of objects on a schedule — the double listing dominates runtime; use S3 Batch Operations, replication, or inventory-driven jobs instead. It is the wrong tool for guaranteed integrity decisions, because it never compares content — if bytes must match, verify checksums separately. And for continuous replication between buckets, S3 Replication does it server-side without a machine in the middle.

Common mistakes

  • Running --delete against the wrong prefix (or the bucket root) and mirroring the mistake instantly — dry-run exists for exactly this.
  • Putting --include before --exclude "*" and wondering why everything copied — filter order is left to right, last match wins.
  • Expecting sync to catch edited files whose size and mtime did not change; it compares metadata, not content.
  • Running one sync per directory in a loop instead of one sync with filters — each invocation pays the listing cost again.

Keeping it under control

  • Wrap production syncs in a script that hardcodes the profile, region, prefix, and a mandatory dry-run step.
  • Turn on versioning for any bucket that is a sync destination with --delete in reach.
  • Put the abort-incomplete-multipart lifecycle rule on upload buckets so interrupted transfers do not bill forever.
  • Pin CLI transfer settings (max_concurrent_requests, multipart_chunksize) in the automation’s own config rather than relying on whatever the host has.

The CLI rewards operators who read the comparison rules once and script defensively ever after — most s3 sync disasters are one missing --dryrun away from being non-events. If nobody on your team truly owns the AWS environment end to end, that is the root of most of these problems — Stack Harbor runs AWS accounts daily under AWS Operations, and you can book an AWS review.