Lesson 3-3 described a fresh security group with no inbound rules, and what could reach the instance through it.
Nothing. Inbound is denied by default, and rules add exceptions to that default.
This is the same default-deny pattern as IAM in lesson 2-2. Hold onto it, because S3 permissions in the next lesson work exactly the same way: a new bucket is reachable by nobody until a policy says otherwise.
Storage without a disk
An EC2 instance stores files on its attached disk, and that disk has problems as a home for important data: it has a fixed size, it lives in one AZ, and if the instance dies badly the data can die with it.
S3 (Simple Storage Service) is a different model called object storage. You do not get a disk. You get an API with roughly two verbs, put this file, get this file:
- A bucket is a named container for files. Bucket names are globally unique across every AWS account on Earth, because they appear in URLs.
- An object is one stored file plus metadata, up to 5 TB.
- A key is the object's full name inside the bucket, like
cats/whiskers.jpg.
S3 automatically stores every object on multiple devices across at least 3 AZs (lesson 1-2 pays off). It is designed for eleven nines of durability, meaning statistically you would wait millions of years to lose one object. Capacity is unlimited, and you pay per GB actually stored, about $0.023 per GB-month.
There are no folders
The key cats/whiskers.jpg looks like a folder path, but S3 is a flat name → object lookup, one giant dictionary. The slashes are just characters in the name that tools display as folders for your comfort.
Every object is addressable by bucket + key, in two spellings:
s3://my-app-photos/cats/whiskers.jpg (CLI style) https://my-app-photos.s3.us-east-1.amazonaws.com/cats/whiskers.jpg (HTTP style)
That second line matters: every object has a real URL. Whether the URL works for the public is a permissions question, coming next lesson.
Building both addresses for one object
Every object is identified by its bucket and its key, and those two values are all you need to write either address form.
bucket="my-app-photos" key="cats/whiskers.jpg" echo "s3://$bucket/$key" echo "https://$bucket.s3.us-east-1.amazonaws.com/$key"
Output
s3://my-app-photos/cats/whiskers.jpg https://my-app-photos.s3.us-east-1.amazonaws.com/cats/whiskers.jpg
The s3:// form is what the CLI and most SDKs take. The HTTPS form is a real URL that a browser can request, and notice that the bucket name appears as a hostname component. That is why bucket names have to be globally unique: they live in DNS.
Addressing an invoice PDF
An invoicing app stores PDFs in acme-invoices, organized by year and month. This builds both addresses for June 2026's invoice 1042.
bucket="acme-invoices" key="2026/06/invoice-1042.pdf" echo "s3://$bucket/$key" echo "https://$bucket.s3.us-east-1.amazonaws.com/$key"
Output
s3://acme-invoices/2026/06/invoice-1042.pdf https://acme-invoices.s3.us-east-1.amazonaws.com/2026/06/invoice-1042.pdf
Reading the key
- Only the two variables changed. The address forms are pure string assembly, so the same two
echolines serve any object. - The key is the whole path-looking string
2026/06/invoice-1042.pdf. The slashes are just part of the name, and no folders named2026or06exist anywhere. - The date-first key layout is a deliberate convention. Because S3 can list objects by key prefix,
2026/06/retrieves exactly one month's invoices, which is the closest thing object storage has to a query. - These URLs are also highly guessable, which is worth remembering before the next lesson on permissions. Invoice 1043 sits at a predictable address.