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

By 

Published on

8 min read

Managing Amazon S3 buckets and objects from the Linux terminal with the aws s3 command

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:

Terminal
aws sts get-caller-identity

If 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:

txt
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:

Terminal
aws s3 ls
output
2025-11-04 09:12:33 my-bucket
2026-01-15 17:40:21 my-site-backups

To list the contents of a bucket, pass the bucket path:

Terminal
aws s3 ls s3://my-bucket
output
                           PRE logs/
2026-02-10 08:30:12    1048576 backup.tar.gz
2026-02-11 09:02:44       4523 notes.txt

Entries 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:

Terminal
aws s3 ls s3://my-bucket --recursive --human-readable --summarize
output
2026-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 MiB

The 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:

Terminal
aws s3 cp backup.tar.gz s3://my-bucket/
output
upload: ./backup.tar.gz to s3://my-bucket/backup.tar.gz

The object keeps its original name. To store it under a different key, spell out the full target path:

Terminal
aws s3 cp backup.tar.gz s3://my-bucket/backups/backup-2026-02-10.tar.gz

Downloading works the same way with the arguments reversed. The . target saves the file into the current directory:

Terminal
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:

Terminal
aws s3 cp s3://my-bucket/backup.tar.gz s3://my-site-backups/backup.tar.gz

Copying Directories

To copy a directory and everything under it, add --recursive:

Terminal
aws s3 cp ./logs s3://my-bucket/logs --recursive

When 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:

Terminal
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 example STANDARD_IA for 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:

Terminal
pg_dump mydb | gzip | aws s3 cp - s3://my-bucket/backups/mydb.sql.gz

Reading works the same way in reverse. This prints an object to standard output where you can pipe it further:

Terminal
aws s3 cp s3://my-bucket/backups/mydb.sql.gz - | gunzip | head

Moving 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:

Terminal
aws s3 mv s3://my-bucket/report-draft.pdf s3://my-bucket/report-final.pdf --dryrun

Check 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:

Terminal
aws s3 sync ./website s3://my-bucket/website

Run 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:

Terminal
aws s3 sync s3://my-bucket/website ./website

Syncing 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:

Terminal
aws s3 sync ./website s3://my-bucket/website --delete --dryrun
output
(dryrun) upload: website/index.html to s3://my-bucket/website/index.html
(dryrun) delete: s3://my-bucket/website/old-page.html

Every 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:

Terminal
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:

Terminal
aws s3 rm s3://my-bucket/old-backup.tar.gz
output
delete: s3://my-bucket/old-backup.tar.gz
Warning
There is no trash bin in S3. Unless the bucket has versioning enabled, a deleted object cannot be recovered. Always test recursive deletions with --dryrun first.

To delete everything under a prefix, add --recursive and preview the damage before committing:

Terminal
aws s3 rm s3://my-bucket/logs/ --recursive --dryrun

If 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:

Terminal
aws s3 mb s3://my-unique-bucket-name --region eu-central-1

The rb (remove bucket) subcommand deletes a bucket, but only when it is empty:

Terminal
aws s3 rb s3://my-unique-bucket-name

Adding --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.

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:

Terminal
aws s3 presign s3://my-bucket/report.pdf --expires-in 3600

The 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 .

TaskCommand
List all bucketsaws s3 ls
List bucket contentsaws s3 ls s3://bucket
List recursively with totalsaws s3 ls s3://bucket --recursive --summarize
Upload a fileaws s3 cp file.txt s3://bucket/
Download a fileaws s3 cp s3://bucket/file.txt .
Upload a directoryaws s3 cp ./dir s3://bucket/dir --recursive
Preview renaming an objectaws s3 mv s3://bucket/old.txt s3://bucket/new.txt --dryrun
Mirror a directoryaws s3 sync ./dir s3://bucket/dir
Preview removing stale files during a mirroraws s3 sync ./dir s3://bucket/dir --delete --dryrun
Delete an objectaws s3 rm s3://bucket/file.txt
Preview deleting a prefixaws s3 rm s3://bucket/dir/ --recursive --dryrun
Create a bucketaws s3 mb s3://bucket
Remove an empty bucketaws s3 rb s3://bucket
Temporary download linkaws 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

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