Sync Files to S3 with AWS CLI
Learn to sync local files to S3 with AWS CLI. This lesson covers the command, flags, and best practices for efficient file transfers.
Focus: sync files to s3 with the aws cli
You've mastered creating buckets, uploading individual files, and setting permissions — but now you're facing the real-world mess: hundreds of local files that need to reach S3 without babysitting each upload. Manually running aws s3 cp for every file is tedious, error-prone, and a surefire way to overwrite newer files with stale ones. The aws s3 sync command eliminates that pain by comparing source and destination, transferring only what changed, and deleting what's no longer needed — all in one deterministic command.
The problem this lesson solves
If you've ever used aws s3 cp to upload a folder, you know the friction: it uploads every file, even ones that haven't changed. That wastes bandwidth, slows down your deploy, and makes incremental backups awkward. Worse, if you delete a file locally, cp won't remove it from S3 — so your bucket slowly becomes a graveyard of stale assets.
The AWS CLI sync command is the fix. It compares the local directory with the S3 prefix, then pushes only new or modified files. With the right flags, it can also delete remote files that no longer exist locally, and even prune empty folders. This turns S3 uploads into a fast, repeatable, and scriptable operation — exactly what you need for deployment pipelines, backup jobs, and content publishing.
Core concept / mental model
Think of aws s3 sync like a two-way mirror (but with a one-way default). It examines the source (typically a local folder) and the destination (an S3 bucket prefix) and reconciles them based on:
- File presence: If a file exists locally but not in S3, it's uploaded.
- Size and modification time: If a file exists in both but differs (size or
LastModified), it's uploaded to make the destination match. - Deletion (only with
--delete): If a file exists in S3 but not locally, it's removed.
The command is stateless — it doesn't keep a manifest or database. It performs a listing of both sides on every run and computes a diff. That's why it's safe to run repeatedly: it's idempotent (running it twice with no changes does nothing).
Pro tip: Think of it as
rsyncfor S3, but with S3's own rules for modification time (S3 storesLastModified, not Unix mtime). Keep that in mind when syncing from filesystems with odd timestamps.
How it works step by step
Here's what happens when you run aws s3 sync:
- List the source — the CLI recursively lists all objects in your local folder.
- List the destination — it lists all objects under the S3 prefix (optionally with
--include/--excludefilters). - Compute the diff — compares relative paths, sizes, and timestamps.
- Upload — files that are new or changed get uploaded (multipart for large files).
- Delete (optional) — with
--delete, files that exist in S3 but not locally are removed. - Report — prints a summary of transfers and deletions.
The comparison logic for a file to upload:
def needs_upload(local_file, s3_object):
if s3_object is None:
return True # doesn't exist in S3
if local_file.size != s3_object.size:
return True # size changed
# AWS CLI uses a threshold: if size differs OR local mtime is newer than S3 LastModified
if local_file.mtime >= s3_object.last_modified:
return True # local is newer
return False
Hands-on walkthrough
Prerequisites
Make sure you have the AWS CLI installed and configured (if not, review earlier lessons):
aws --version
aws configure # sets access key, secret, region
Basic sync: upload local folder to S3
Create a local folder with a few files, then sync it to a bucket:
mkdir -p ~/my-site/assets
echo 'body { color: red; }' > ~/my-site/assets/style.css
echo 'console.log("hi")' > ~/my-site/assets/app.js
aws s3 sync ~/my-site/assets s3://my-bucket/assets/
Expected output (first run):
upload: assets/style.css to s3://my-bucket/assets/style.css
upload: assets/app.js to s3://my-bucket/assets/app.js
Now modify style.css and run again — only that file uploads:
echo 'body { color: blue; }' > ~/my-site/assets/style.css
aws s3 sync ~/my-site/assets s3://my-bucket/assets/
Output: only style.css is uploaded.
Sync with deletion (mirror your local folder)
If you delete a local file and want S3 to match, use --delete:
rm ~/my-site/assets/app.js
aws s3 sync ~/my-site/assets s3://my-bucket/assets/ --delete
Output includes a delete: line.
Sync with filters (exclude temp files)
Use --exclude and --include to skip logs or caches:
aws s3 sync . s3://my-bucket/site/ --exclude "*.tmp" --exclude "node_modules/*"
You can combine multiple filters; order matters — the first matching pattern wins.
Sync from S3 to local (download)
The command is symmetric — swap source and destination:
aws s3 sync s3://my-bucket/backups/ ./backups/
Full example with flags
aws s3 sync ./build/ s3://my-app-frontend/ --delete --exclude "*.map" --exclude "*.DS_Store" --size-only
--size-onlycompares only file size, ignoring timestamps (useful if mtimes are off).--excludeprevents uploading source maps and macOS metadata.--deleteprunes stale files.
Compare options / when to choose what
| Command | Uploads everything? | Deletes extras? | Use case |
|---|---|---|---|
aws s3 cp --recursive |
Yes, always | No | One-off full copy; small folders |
aws s3 sync |
Only changed | With --delete |
Incremental deploys, backups, static site publishing |
aws s3api put-object |
Single file | No | Fine-grained control, custom script |
For most workflows, sync is the go-to because it's efficient and safe. Use cp when you explicitly want to overwrite unconditionally (e.g., forcing a re-upload of corrupted files). Use s3api when you need to set metadata or encryption at upload time.
Troubleshooting & edge cases
- Accidental deletions — Running
sync --deletewithout thinking can wipe files you meant to keep. Always do a dry run first:bash aws s3 sync ./build s3://bucket --delete --dryrun - Newer local file doesn't upload — If your local mtime is older than S3
LastModified, the CLI skips it. Use--size-onlyto force uploads when content changed but timestamps are misleading. - Permission errors —
Upload failed: AccessDenied. Check your IAM policy: you needs3:PutObject,s3:ListBucket, and optionallys3:DeleteObjectfor--delete. - S3
LastModifiedis updated on copy or encryption changes — Running sync can re-upload files even if content is identical if S3's timestamp changed. Use--size-onlyto avoid needless transfers. - Many small files — Sync can be slow with thousands of tiny files. Use
aws s3 sync --multipart-uploadflags or consider compressing into larger files and using--only-show-errorsfor cleaner output.
What you learned & what's next
You now know how to sync files to S3 with the AWS CLI — comparing local and remote state, transferring only diffs, and optionally deleting stale objects. You saw how filters control what gets transferred, and how --dryrun prevents costly mistakes. This is the foundation for automated deploys and backups.
Next lesson in the AWS Tutorial track covers S3 event notifications and Lambda triggers — where synced files become the spark for serverless processing. With sync, you can reliably push data, and events let you react to it.
Practice recap
Create a local folder with mixed file types (HTML, CSS, JS, and a temp file). Sync it to a bucket using --exclude "*.tmp" and verify only the intended files appear. Then modify one file, re-sync, and confirm only that file uploads. Finally, add --delete and remove a local file, then check the bucket to confirm it was deleted.
Common mistakes
- Running
sync --deletewithout a--dryrunfirst — one wrong path and you can wipe production files. - Using
syncwithout--size-onlywhen timestamps are unreliable, causing unnecessary uploads or missed updates. - Forgetting to add IAM permissions for
s3:DeleteObjectwhen using--delete; the sync silently fails or only uploads. - Relying on mtime comparisons across systems — S3's
LastModifiedand your local clock can drift, so use--size-onlywhen consistency matters more than time.
Variations
- Use
aws s3 sync --excludeand--includepatterns to selectively sync only certain file types, like--exclude ".tmp" --include ".css". - Use
--size-onlyto ignore modification times and stale timestamps, which is helpful for CDN asset publishing. - With large sets of files, parallelize with
--multipart-uploadflags or split into multiple sync commands per subdirectory for faster transfer.
Real-world use cases
- Deploying a static website: sync the
build/folder to your S3 bucket after a CI build. - Automated database backups: sync local backup files to a versioned S3 bucket for durable storage.
- Sharing datasets across teams: sync a local data directory to a shared S3 prefix so teammates can pull updates.
Key takeaways
aws s3 synccompares source and destination and uploads only new or modified files.- Use
--deleteto mirror deletions from local to S3 — but always dry-run first. --excludeand--includeact like filter rules that control exactly which files transfer.--size-onlylets you ignore timestamps and compare purely by file size.- The command is idempotent — running it repeatedly with no changes produces no transfers.
- S3's
LastModifieddiffers from local mtime — be deliberate about how you compare.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.