HomebaseWorks
Lesson 3 of 4

S3: buckets, storage classes and safe access

How S3 stores objects, which storage class to pick, how versioning and lifecycle work, and how to grant access without going public.

EWritten by the HomebaseWorks editorial team · reviewed 26 Oct 2026

S3 is the service almost every AWS job touches, and the one most often misconfigured. A bucket left public is the classic cloud incident. This lesson covers the model, the choices that cost money, and the way to give an application access while keeping Block Public Access on.

Advertisement

The object model

A bucket has a name that is unique across all of AWS and lives in one region. It holds objects, each identified by a key such as invoices/2026/03/inv-1042.pdf. There are no real folders; the slashes are part of the key, and the console just groups by prefix. The CLI mirrors the console:

aws s3 ls s3://acme-invoices/invoices/2026/
aws s3 cp report.csv s3://acme-invoices/reports/
aws s3 sync ./build s3://acme-static-site --delete
aws s3 presign s3://acme-invoices/invoices/inv-1042.pdf --expires-in 900

sync uploads only changed files, and --delete removes remote files missing locally, so read the dry-run first. presign creates a time-limited link so a user can download one private object without any AWS credentials.

Storage classes, versioning and lifecycle

Standard is for data read often. Standard-IA costs less to store but charges per retrieval, so it suits backups read rarely. The Glacier classes are for archives where waiting minutes or hours to retrieve is acceptable. Intelligent-Tiering moves objects between tiers for you when access patterns are unknown. Turn on versioning for anything a human edits; an overwrite or delete then keeps the old version and you can restore it. Pair it with a lifecycle rule, for example: move objects to Standard-IA after 30 days, to Glacier after 180, and expire old versions after 90 days. Without the expiry rule, versioning quietly doubles your bill.

Access without going public

Keep Block Public Access enabled at the account level. Grant access with an IAM role for the application and, when needed, a bucket policy that names that role:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"AWS": "arn:aws:iam::123456789012:role/app-role"},
    "Action": ["s3:GetObject", "s3:PutObject"],
    "Resource": "arn:aws:s3:::acme-invoices/uploads/*"
  }]
}

Note that ListBucket applies to the bucket ARN, while GetObject applies to the object ARN with /*. Mixing them up is the most common policy error. Server-side encryption is on by default; leave it on. For a public website, use CloudFront in front of the bucket rather than opening the bucket itself.

Common mistakes and a practice habit

  • Choosing Standard-IA for files read daily, which costs more than Standard once retrieval fees are added.
  • Enabling versioning with no expiry rule.
  • Putting "Principal": "*" in a policy to make an error go away.
  • Storing access keys in code to reach S3 instead of attaching a role.

Practice habit. In a free account, create a bucket, upload a file, enable versioning, overwrite the file, then restore the first version. Write a policy for a role and test it with a presigned URL. Delete everything after, and check the billing page the next day.

Advertisement
Continue with AWS basics4 lessons · 1 interview set · 1 test · 1 certificate
Not tested

More in Cloud and DevOps