On this page
S3 — Object Storage
Amazon Simple Storage Service (S3) is object storage built for unlimited scale. Store images, backups, logs, data lakes, and static websites — S3 handles 100 million requests per second across AWS globally.
Core Concepts
| Concept | Description |
|---|---|
| Bucket | Global namespace container (name must be unique worldwide) |
| Object | File + metadata, up to 5 TB |
| Key | Object path within bucket (images/logo.png) |
| Storage Class | Cost/availability tier (Standard, IA, Glacier) |
| Region | Bucket is created in one region (except CloudFront origin) |
Create a Bucket and Upload
# Create bucket (name must be globally unique)
aws s3 mb s3://my-company-app-assets-2024 --region us-east-1
# Upload a file
aws s3 cp ./logo.png s3://my-company-app-assets-2024/images/logo.png
# Upload directory recursively
aws s3 sync ./dist s3://my-company-app-assets-2024/ --delete
# List objects
aws s3 ls s3://my-company-app-assets-2024/ --recursive --human-readable
Storage Classes Comparison
| Class | Durability | Access Pattern | Retrieval | Cost |
|---|---|---|---|---|
| Standard | 99.999999999% | Frequent | Instant | Highest storage |
| Standard-IA | 99.999999999% | Infrequent | Instant | Lower storage, retrieval fee |
| One Zone-IA | 99.5% (single AZ) | Infrequent, recreatable | Instant | Cheapest IA |
| Glacier Instant | 99.999999999% | Archive, instant access | Instant | Archive pricing |
| Glacier Flexible | 99.999999999% | Archive | Minutes–hours | Very low storage |
| Glacier Deep Archive | 99.999999999% | Long-term archive | 12–48 hours | Lowest storage |
# Upload directly to Intelligent-Tiering (auto-moves between tiers)
aws s3 cp report.pdf s3://my-bucket/archives/ \
--storage-class INTELLIGENT_TIERING
Versioning and MFA Delete
Protect against accidental deletion:
aws s3api put-bucket-versioning \
--bucket my-company-app-assets-2024 \
--versioning-configuration Status=Enabled
# List all versions of an object
aws s3api list-object-versions \
--bucket my-company-app-assets-2024 \
--prefix images/logo.png
Lifecycle Policies
Automate transitions and expiration:
{
"Rules": [{
"ID": "MoveLogsToGlacier",
"Status": "Enabled",
"Filter": {"Prefix": "logs/"},
"Transitions": [{
"Days": 30,
"StorageClass": "STANDARD_IA"
}, {
"Days": 90,
"StorageClass": "GLACIER"
}],
"Expiration": {"Days": 365}
}]
}
aws s3api put-bucket-lifecycle-configuration \
--bucket my-company-app-assets-2024 \
--lifecycle-configuration file://lifecycle.json
Security Best Practices
Block Public Access (Default Since 2023)
aws s3api put-public-access-block \
--bucket my-company-app-assets-2024 \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
Bucket Policy (CloudFront OAC Example)
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowCloudFrontOAC",
"Effect": "Allow",
"Principal": {"Service": "cloudfront.amazonaws.com"},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-company-app-assets-2024/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E1234567890"
}
}
}]
}
Encryption
# Default encryption (SSE-S3 or SSE-KMS)
aws s3api put-bucket-encryption \
--bucket my-company-app-assets-2024 \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:us-east-1:123:key/xxx"
}
}]
}'
Static Website Hosting
# Enable website hosting
aws s3 website s3://my-company-app-assets-2024/ \
--index-document index.html \
--error-document error.html
# Sync React/Vue build output
npm run build
aws s3 sync ./build s3://my-company-app-assets-2024/ \
--cache-control "max-age=31536000" \
--exclude "index.html"
aws s3 cp ./build/index.html s3://my-company-app-assets-2024/index.html \
--cache-control "no-cache"
In production, front CloudFront for HTTPS, custom domain, and edge caching.
Presigned URLs
Grant temporary access without making objects public:
# URL valid for 1 hour
aws s3 presign s3://my-bucket/private/report.pdf --expires-in 3600
import boto3
s3 = boto3.client('s3')
url = s3.generate_presigned_url(
'get_object',
Params={'Bucket': 'my-bucket', 'Key': 'private/report.pdf'},
ExpiresIn=3600
)
S3 Event Notifications
Trigger Lambda, SQS, or SNS on object events:
aws s3api put-bucket-notification-configuration \
--bucket my-bucket \
--notification-configuration '{
"LambdaFunctionConfigurations": [{
"LambdaFunctionArn": "arn:aws:lambda:us-east-1:123:function:ProcessUpload",
"Events": ["s3:ObjectCreated:*"],
"Filter": {"Key": {"FilterRules": [{"Name": "prefix", "Value": "uploads/"}]}}
}]
}'
Real-World Scenario: Media Platform
| Bucket | Purpose | Class | Lifecycle |
|---|---|---|---|
media-uploads |
User uploads | Standard | Delete incomplete multipart after 7 days |
media-processed |
Transcoded video | Standard | Move to IA after 90 days |
media-archive |
Old content | Glacier | 7-year retention for compliance |
app-logs |
Access logs | Standard | Glacier after 30 days, delete after 1 year |
S3 vs EBS vs EFS
| Service | Type | Access | Scale |
|---|---|---|---|
| S3 | Object | HTTP/API | Unlimited objects |
| EBS | Block | Single EC2 instance | Up to 64 TiB per volume |
| EFS | File (NFS) | Multiple EC2 instances | Petabyte scale |
Common Mistakes
- Public buckets — verify Block Public Access and bucket policies
- No versioning on critical data — enable versioning for production buckets
- Ignoring lifecycle rules — old logs in Standard class waste money
- Sync without
--delete— stale files remain after redeploys - Large number of small objects — consider packing into larger files or using S3 Express One Zone for high-throughput
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
403 Forbidden |
IAM or bucket policy | Check policy, Block Public Access, object ACLs |
NoSuchBucket |
Wrong region or name | S3 is regional; verify bucket name and region |
| Slow uploads | No transfer acceleration | Enable S3 Transfer Acceleration for cross-region |
| High costs | Standard class for archives | Apply lifecycle rules to IA/Glacier |
AccessDenied on CloudFront |
Missing OAC/OAI | Update bucket policy for CloudFront distribution |
Best Practices
- Enable versioning and MFA Delete on production buckets
- Use SSE-KMS for sensitive data with key rotation
- Apply lifecycle policies from day one
- Front public content with CloudFront + OAC
- Use S3 Inventory for auditing object metadata at scale
- Enable S3 Access Logging for security forensics
- Use multipart upload for files > 100 MB
Next: RDS — Managed Databases.