← All posts
2 min read

Build a private S3 backup bucket from the CLI

#aws#cli#s3#storage#project#security
📑 On this page

Part 80 of AWS from Zero. This S3 capstone assembles the small commands from Parts 11–79 into one private backup workflow.

What we are learning

A useful backup bucket needs more than an upload command: private access, ownership, encryption, version recovery, stale-upload cleanup, verification, and an explicit deletion plan.

Before you run it

REGION="ap-south-1"
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
BUCKET="aws-zero-backup-$ACCOUNT_ID-$REGION"

Review pricing and choose a unique bucket name. The example uses general purpose S3 and SSE-S3 to keep the first capstone focused.

Cost note: Versioning stores old copies, and backup growth must be monitored with lifecycle and cost controls.

The command

aws s3api create-bucket \
  --bucket "$BUCKET" \
  --region "$REGION" \
  --create-bucket-configuration LocationConstraint="$REGION"
 
aws s3api put-bucket-ownership-controls \
  --bucket "$BUCKET" \
  --ownership-controls "Rules=[{ObjectOwnership=BucketOwnerEnforced}]"
 
aws s3api put-public-access-block \
  --bucket "$BUCKET" \
  --public-access-block-configuration \
  BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
 
aws s3api put-bucket-encryption \
  --bucket "$BUCKET" \
  --server-side-encryption-configuration \
  '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'
 
aws s3api put-bucket-versioning \
  --bucket "$BUCKET" \
  --versioning-configuration Status=Enabled
 
cat > backup-lifecycle.json <<'EOF'
{
  "Rules": [{
    "ID": "AbortStaleMultipartUploads",
    "Status": "Enabled",
    "Filter": {"Prefix": ""},
    "AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 7}
  }]
}
EOF
 
aws s3api put-bucket-lifecycle-configuration \
  --bucket "$BUCKET" \
  --lifecycle-configuration file://backup-lifecycle.json

The bucket now has policy-based ownership, public-access guardrails, explicit encryption, version history, and stale multipart cleanup.

Inspect the result

aws s3api get-public-access-block --bucket "$BUCKET"
aws s3api get-bucket-versioning --bucket "$BUCKET"
aws s3api get-bucket-encryption --bucket "$BUCKET"
aws s3api get-bucket-ownership-controls --bucket "$BUCKET"
aws s3api get-bucket-lifecycle-configuration --bucket "$BUCKET"

Treat every inspection as a deployment assertion. A backup is not ready merely because the bucket exists.

One tiny variation

aws s3 sync backup-source/ "s3://$BUCKET/backups/" \
  --exclude "*.tmp" \
  --checksum-mode ENABLED \
  --dryrun

Preview the first backup, then remove --dryrun and verify object counts, sizes, and a test restore.

Common mistake

A synchronized copy is not a complete backup strategy. Define retention, restore testing, credential isolation, monitoring, and protection from accidental or malicious deletion.

Cleanup

aws s3api list-object-versions \
  --bucket "$BUCKET" \
  --query "{Versions:Versions[].{Key:Key,VersionId:VersionId},Markers:DeleteMarkers[].{Key:Key,VersionId:VersionId}}"
# Permanently delete every listed version and marker before deleting the bucket.
rm backup-lifecycle.json

Versioned buckets require version-aware cleanup. Do not run broad permanent deletion commands against a real backup bucket.

Next, we will learn Read your AWS caller identity deeply.

Official AWS CLI reference