How to delete an Amazon S3 bucket, and why it keeps saying the bucket is not empty
Deleting an S3 bucket fails until every object, every old version, every delete marker and every unfinished upload is gone, and until nothing else holds it. Here are the console, CLI and lifecycle routes, the errors each one hits, and the checks to run before a delete you cannot undo.
An S3 bucket deletes only when it holds nothing: no objects, no versions, no delete markers, no unfinished uploads, no same-account access points, and no Deny on s3:DeleteBucket from a bucket policy, SCP or RCP. The console's Empty handles versions, aws s3 rb --force does not, and for millions of objects a one-day lifecycle rule with all four expiration actions is the cheapest path. Object Lock retention cannot be skipped, and because bucket names are global and reusable by anyone, AWS's own advice for a bucket you have stopped using is to empty it and keep it.
"aws s3 delete bucket" is one of the few S3 searches that stays steady all year, and it is usually typed right after a failed attempt. The console says the bucket is not empty. The CLI says BucketNotEmpty. You are sure you deleted everything, and S3 is sure you did not.
Both are right. A bucket can only be deleted when it contains nothing at all, and "nothing" includes things the default views do not show: old object versions, delete markers, and the parts of uploads that never finished. This guide walks through what has to be gone, the three ways to empty a bucket, the non-object reasons a delete is refused, and the one question to answer before you start.
Decide whether you should delete it at all
Deleting a bucket is permanent and AWS cannot restore it. Less obviously, general purpose bucket names live in a global namespace, so once your bucket is gone any other AWS account can create a bucket with the same name and receive requests that were meant for yours: a forgotten backup script, a load balancer still writing logs, a link in an old email. AWS's own recommendation is to empty a bucket you no longer use and keep it, so the name stays yours and the storage bill drops to nothing.
If the bucket holds business files rather than build artifacts, treat this as an archive decision first. Files that might be needed again belong in a cheaper storage class, not in the trash.
A guide to archiving files in Amazon S3Recover deleted or overwritten files with S3 Versioning
- Deleted buckets cannot be restored by AWS.
- Another account can reuse the name and receive your stragglers.
- An empty bucket costs nothing to keep; keeping it is AWS's recommendation.
What has to be gone before S3 lets you delete
DeleteBucket succeeds only when the bucket has no objects, no object versions, no delete markers and, for directory buckets, no in-progress multipart uploads. On a versioned bucket a normal delete does not remove anything: it adds a delete marker on top of the object, which then counts as content. Incomplete multipart uploads sit outside the object list entirely and are billed as storage until aborted.
The bucket also has to be free of same-account S3 Access Points and Multi-Region Access Points, and your identity needs s3:DeleteBucket, with no Deny statement overriding it. Each of those produces a different error, so the message tells you which list to check.
- Objects, including everything moved to Glacier storage classes.
- Noncurrent versions and delete markers, if versioning is or ever was enabled.
- Incomplete multipart uploads.
- Same-account access points and Multi-Region Access Points.
- An allowed s3:DeleteBucket, not denied by bucket policy, SCP or RCP.
Route 1: the console, for small buckets
In the S3 console choose the bucket, choose Empty, type the bucket name to confirm, and watch the status page until it finishes. On a bucket with versioning enabled or suspended, Empty deletes all versions and delete markers, which is more than the CLI does. Then choose Delete, type the name again and confirm. If the bucket still has objects, the Delete page shows an error with an Empty bucket button that takes you straight back.
The console path works well up to a few hundred thousand objects. Beyond that it is slow, and anything uploaded while the empty runs may or may not be removed.
- Empty handles versions and delete markers; the CLI's rm does not.
- Objects added during the empty may be deleted too.
Route 2: the CLI, with the versioning catch
For an unversioned bucket a single command deletes every object and then the bucket. For a versioned bucket the same command fails, because the force flag deletes current objects only and leaves versions behind. AWS's documentation is explicit that this causes the delete to fail with a non-empty bucket. You remove versions and delete markers with the lower-level s3api commands, abort any unfinished uploads, and then remove the bucket.
# Unversioned bucket: delete every object, then the bucket
aws s3 rb s3://acme-old-exports --force
# Versioned bucket: permanently delete all versions and delete markers first
aws s3api list-object-versions --bucket acme-old-exports \
--query '{Objects: [Versions[].{Key:Key,VersionId:VersionId}, DeleteMarkers[].{Key:Key,VersionId:VersionId}][]}' \
--output json > versions.json
aws s3api delete-objects --bucket acme-old-exports --delete file://versions.json
# Abort unfinished multipart uploads
aws s3api list-multipart-uploads --bucket acme-old-exports \
--query 'Uploads[].{Key:Key,UploadId:UploadId}' --output text |
while read -r key id; do
aws s3api abort-multipart-upload --bucket acme-old-exports --key "$key" --upload-id "$id"
done
aws s3 rb s3://acme-old-exportsaws s3 cp and aws s3 ls: recursive examples
- delete-objects takes up to 1,000 keys per call; list-object-versions pages at 1,000, so loop with the returned markers for big buckets.
- Deleting a specific version needs s3:DeleteObjectVersion, and the bucket owner identity if MFA delete is on.
- Run the list command once without delete-objects and read what is about to go.
Route 3: a lifecycle rule, for large buckets
For millions of objects AWS recommends letting S3 Lifecycle do the deleting. One rule with no prefix filter and four actions covers everything: expire current versions after one day, permanently delete noncurrent versions after one day, delete expired object delete markers, and abort incomplete multipart uploads after one day. Lifecycle runs asynchronously, so the bucket may take a few days to empty, but you stop being billed for an object as soon as it is marked for deletion.
On a versioned bucket the expiration action does not delete the current version. It adds a delete marker and the object becomes noncurrent, which is why the noncurrent-version and delete-marker actions have to be in the same rule. Once the bucket reports zero objects and zero versions, the rule has done its job and the bucket can go.
S3 lifecycle rules examples for versioned buckets
- Four actions in one rule: expire current, delete noncurrent, remove expired delete markers, abort multipart.
- Takes days, costs nothing extra, and needs no script to babysit.
- If a CloudTrail trail logs into the same bucket, stop it first or it refills the bucket while you empty it.
When the bucket is empty and the delete still fails
An access point attached from the same account blocks deletion; list and delete the bucket's access points and any Multi-Region Access Point, then retry. An AccessDenied on the delete itself usually means a Deny statement rather than a missing Allow. Buckets created by Elastic Beanstalk carry a bucket policy that denies s3:DeleteBucket by default, and organisations often add the same Deny in a service control policy or resource control policy to protect production data. In both cases you remove the statement, or ask the person who owns it to, before the delete can succeed.
Object Lock is the hard stop. A version under a retention period or legal hold returns 403 to a permanent delete, so the bucket cannot be emptied until the retention expires or the hold is removed. In compliance mode nobody, including the root user, can shorten that; AWS notes the only way out early is to close the AWS account.
Amazon S3 Object Lock: governance vs compliance modeS3 bucket policy examples for team file access
- Access points: delete them first, then the bucket.
- AccessDenied: look for Deny on s3:DeleteBucket in the bucket policy, SCPs and RCPs.
- Object Lock: wait out the retention or remove the legal hold; compliance mode cannot be overridden.
Before you press Delete
A short checklist for the five minutes before an irreversible action. Most of it is about the things that will keep writing to the name after it is gone.
- Export or replicate anything that might be asked for again; S3 cannot undelete a bucket.
- Stop writers: ELB and CloudTrail logging, backup jobs, CI pipelines, scheduled syncs.
- Remove Route 53 records if the bucket hosted a static website.
- Check for a CloudFront distribution, replication rule or event notification that points at the bucket.
- Prefer emptying over deleting if the name appears anywhere you cannot change.
Where a workspace view helps
Deleting a bucket is an account-owner action, and the roles a file workspace like BucketDesk assumes are deliberately scoped to the files, not to bucket lifecycle. What a workspace does help with is the step before: seeing what is actually in the bucket, including versions and what each one is, previewing files without downloading them, and letting the people who own the documents say what still matters. BucketDesk's archive jobs move the keepers to a cheaper storage class on the customer's own account, so emptying the bucket afterwards is a decision about leftovers rather than a gamble.
Preview files in Amazon S3 without downloading themFeatures: archive and restore
Minimum is free. Deploy a scoped role with CloudFormation, sign in, and browse, without handing anyone an access key.
Primary sources
Discussion
0 comments · open to guests · moderatedLiked this? Get the next article by email. No schedule, no filler, one click to leave.