aws s3 Commands: cp, sync, ls, and rm Examples

Uploading a file to Amazon S3 through the web console works fine once. The tenth time, or the first time you need to move a whole directory tree, you want it in your shell history instead. The aws s3 command group gives you familiar Unix-style operations for S3: ls to list, cp to copy, sync to mirror directories, and rm to delete.
This guide shows practical examples of the most used aws s3 commands, including recursive copies, include and exclude filters, and safe deletion with --dryrun.
Prerequisites
The examples require AWS CLI version 2 and an identity with permission to access the S3 resources you use. If the CLI is not ready yet, follow our guide on installing and configuring the AWS CLI on Linux .
Confirm that the CLI can authenticate to your account with:
aws sts get-caller-identityIf the command prints your account ID and IAM identity, authentication is working. Individual S3 commands can still return AccessDenied when that identity lacks the required bucket or object permissions.
The AWS CLI has two command groups for S3. The aws s3 group used in this guide provides high-level file operations. The aws s3api group exposes the raw API, one call per operation, and is only needed for tasks such as managing bucket policies or object versions.
S3 paths use the form s3://bucket-name/key. The general command structure is:
aws s3 <subcommand> [ARGUMENTS] [OPTIONS]Commands such as cp, mv, and sync take source and destination arguments. The remaining commands operate on one S3 path, except aws s3 ls, which can omit the path to list every bucket.
Listing Buckets and Objects
Run aws s3 ls with no arguments to list all buckets in the account:
aws s3 ls2025-11-04 09:12:33 my-bucket
2026-01-15 17:40:21 my-site-backupsTo list the contents of a bucket, pass the bucket path:
aws s3 ls s3://my-bucket PRE logs/
2026-02-10 08:30:12 1048576 backup.tar.gz
2026-02-11 09:02:44 4523 notes.txtEntries marked PRE are prefixes, the S3 equivalent of directories. To look inside one, append it to the path with a trailing slash, for example aws s3 ls s3://my-bucket/logs/.
Add --recursive to walk the whole bucket, and combine it with --human-readable and --summarize to get readable sizes and totals:
aws s3 ls s3://my-bucket --recursive --human-readable --summarize2026-02-10 08:30:12 1.0 MiB backup.tar.gz
2026-02-11 09:02:44 4.4 KiB notes.txt
2026-02-12 06:15:09 12.7 MiB logs/app.log
Total Objects: 3
Total Size: 13.7 MiBThe summary at the bottom shows the count and combined size of the current objects listed under the bucket or prefix. It does not include noncurrent versions in a versioned bucket.
Uploading and Downloading Files with cp
The cp subcommand copies files between your machine and S3, in either direction. To upload a file to a bucket:
aws s3 cp backup.tar.gz s3://my-bucket/upload: ./backup.tar.gz to s3://my-bucket/backup.tar.gzThe object keeps its original name. To store it under a different key, spell out the full target path:
aws s3 cp backup.tar.gz s3://my-bucket/backups/backup-2026-02-10.tar.gzDownloading works the same way with the arguments reversed. The . target saves the file into the current directory:
aws s3 cp s3://my-bucket/backup.tar.gz .You can also copy directly between two buckets. The transfer happens inside AWS, so the data does not pass through your machine:
aws s3 cp s3://my-bucket/backup.tar.gz s3://my-site-backups/backup.tar.gzCopying Directories
To copy a directory and everything under it, add --recursive:
aws s3 cp ./logs s3://my-bucket/logs --recursiveWhen you only want some of the files, combine --exclude and --include. The filters are evaluated in the order given, and the later filter wins, so the usual pattern is to exclude everything and then include what you want:
aws s3 cp ./logs s3://my-bucket/logs --recursive --exclude "*" --include "*.log"This uploads only the .log files and skips everything else in the directory.
The most useful cp options:
--recursive- Copy all files under a directory or prefix.--exclude/--include- Filter files by pattern; later filters override earlier ones.--storage-class- Choose the destination storage class, for exampleSTANDARD_IAfor backups accessed infrequently.--dryrun- Print what would be copied without transferring anything.
Streaming to and from S3
The cp command accepts - as a stand-in for standard input or output, which lets you pipe data straight to a bucket without a temporary file:
pg_dump mydb | gzip | aws s3 cp - s3://my-bucket/backups/mydb.sql.gzReading works the same way in reverse. This prints an object to standard output where you can pipe it further:
aws s3 cp s3://my-bucket/backups/mydb.sql.gz - | gunzip | headMoving and Renaming Objects with mv
Amazon S3 does not rename an object in place. The mv command copies the source to the new key and then deletes the source, so preview the operation before running it:
aws s3 mv s3://my-bucket/report-draft.pdf s3://my-bucket/report-final.pdf --dryrunCheck both paths carefully, then run the command again without --dryrun. The same command can move files between your machine and S3 or between two buckets.
Synchronizing Directories with sync
Where cp --recursive copies everything every time, sync transfers files that are missing, differ in size, or have a newer source modification time. To mirror a local directory to a bucket:
aws s3 sync ./website s3://my-bucket/websiteRun it again immediately and nothing is transferred, because both sides already match. This makes sync the right tool for repeated jobs such as publishing a static site or shipping nightly backups.
The reverse direction downloads new and changed objects from the bucket:
aws s3 sync s3://my-bucket/website ./websiteSyncing between two buckets also works: aws s3 sync s3://my-bucket s3://my-site-backups.
By default, sync never deletes anything. Files removed from the source remain on the target. To make the target an exact mirror, add --delete, but preview the result first because deleted objects are gone for good:
aws s3 sync ./website s3://my-bucket/website --delete --dryrun(dryrun) upload: website/index.html to s3://my-bucket/website/index.html
(dryrun) delete: s3://my-bucket/website/old-page.htmlEvery line is prefixed with (dryrun), so nothing has happened yet. Review the delete: lines, and when the plan looks right, run the same command without --dryrun.
The same --exclude and --include filters from cp apply here. A common one keeps repository metadata out of the bucket:
aws s3 sync ./website s3://my-bucket/website --exclude ".git/*"Because sync is idempotent, it pairs well with a scheduled job. See our guide on scheduling cron jobs with crontab
if you want to run a sync every night.
Deleting Objects with rm
To delete a single object, pass its full path:
aws s3 rm s3://my-bucket/old-backup.tar.gzdelete: s3://my-bucket/old-backup.tar.gz--dryrun first.To delete everything under a prefix, add --recursive and preview the damage before committing:
aws s3 rm s3://my-bucket/logs/ --recursive --dryrunIf the listed objects are the ones you expect, run the command again without --dryrun to perform the deletion.
On buckets with versioning enabled, rm only inserts a delete marker; previous versions remain and continue to incur storage costs. Removing versions permanently requires the aws s3api delete-object call with a version ID.
Creating and Removing Buckets
The mb (make bucket) subcommand creates a new bucket. General purpose bucket names must be unique across AWS accounts and Regions within an AWS partition, so pick something difficult to collide with:
aws s3 mb s3://my-unique-bucket-name --region eu-central-1The rb (remove bucket) subcommand deletes a bucket, but only when it is empty:
aws s3 rb s3://my-unique-bucket-nameAdding --force deletes the current objects in an unversioned bucket and then removes the bucket. It does not permanently delete object versions or delete markers, so removal fails when a versioned bucket still contains them. Treat --force with the same caution as rm --recursive.
Generating Temporary Download Links
Sometimes you need to hand a file to someone without making the bucket public. The presign subcommand generates a URL that grants temporary access to a single object:
aws s3 presign s3://my-bucket/report.pdf --expires-in 3600The command prints a long URL that anyone can use to download the object for the next hour (3600 seconds). The default lifetime is one hour, and the maximum is seven days. A URL signed with temporary credentials expires when those credentials expire, even when --expires-in requests a later time.
Quick Reference
For a printable quick reference, see the AWS CLI cheatsheet .
| Task | Command |
|---|---|
| List all buckets | aws s3 ls |
| List bucket contents | aws s3 ls s3://bucket |
| List recursively with totals | aws s3 ls s3://bucket --recursive --summarize |
| Upload a file | aws s3 cp file.txt s3://bucket/ |
| Download a file | aws s3 cp s3://bucket/file.txt . |
| Upload a directory | aws s3 cp ./dir s3://bucket/dir --recursive |
| Preview renaming an object | aws s3 mv s3://bucket/old.txt s3://bucket/new.txt --dryrun |
| Mirror a directory | aws s3 sync ./dir s3://bucket/dir |
| Preview removing stale files during a mirror | aws s3 sync ./dir s3://bucket/dir --delete --dryrun |
| Delete an object | aws s3 rm s3://bucket/file.txt |
| Preview deleting a prefix | aws s3 rm s3://bucket/dir/ --recursive --dryrun |
| Create a bucket | aws s3 mb s3://bucket |
| Remove an empty bucket | aws s3 rb s3://bucket |
| Temporary download link | aws s3 presign s3://bucket/file.txt |
FAQ
What is the difference between cp –recursive and sync?cp --recursive copies every file on every run. sync compares both sides and transfers only new or changed files, which is faster and cheaper for repeated jobs.
Can I use shell wildcards in S3 paths?
No. The AWS CLI does not interpret Unix-style wildcards in an S3 path such as s3://bucket/*.log. Use --recursive together with --exclude and --include filters instead.
How do I delete all files in a bucket?
For an unversioned bucket, preview aws s3 rm s3://bucket --recursive --dryrun, then remove --dryrun to empty it. The aws s3 rb s3://bucket --force command can empty and remove an unversioned bucket in one step, but it fails when object versions or delete markers remain.
Conclusion
Make --dryrun a habit before mv, recursive rm, or sync --delete, then reuse the reviewed commands in your scripts and scheduled jobs.
Tags
Linuxize Weekly Newsletter
A quick weekly roundup of new tutorials, news, and tips.
About the authors

Dejan Panovski
Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.
View author page