VPC - An analogy about shared resources
Public subnets and Internet Gateway
Load balancing - one front door, many clerks
<aside> 🚧
This page is currently under construction. Please check back later.
</aside>
<aside> 🧱
Step 3 of 3 — building the week's architecture
Availability Zone A is done: a public subnet with hyf-web and your hyf-bastion jump box, a private subnet with hyf-app. This section adds the last tier — a private subnet in a second Availability Zone holding the database hyf-db — and then locks the whole thing down.
Two new tools do that work: VPC endpoints, so the most protected subnet needs no NAT Gateway at all, and Security Groups, so each machine accepts traffic only from the one tier that is supposed to talk to it.
After this page, the diagram from the Week 4 overview is complete.
</aside>
So far every machine you built can talk to every other machine, because every security group allows the whole VPC CIDR and every subnet shares the same routes. That is a network, but it is not yet an architecture.
The difference is intent. In a real system you can say, out loud, who is allowed to talk to whom:
The internet may talk to the web server, and only on port 80. The web server may talk to the app server, and only on port 3000. The app server may talk to the database, and only on port 5432. You may log in to the bastion, and only the bastion may log in to anything else. Nothing else is allowed, in any direction.
This section is about making AWS enforce those five sentences. We do it in three moves: put the database somewhere structurally harder to reach (a private subnet in another AZ), give it the one outside service it genuinely needs without giving it the internet (endpoints), and then write the rules themselves (security groups).
An Availability Zone is one or more physically separate data centres — separate power, separate cooling, separate network. Putting your database in a different AZ from your application means a fire, flood or power failure in AZ A does not take your data with it.
Two things are worth understanding before you click anything:
local route covering the whole VPC CIDR (10.0.0.0/16), and that route covers every subnet in every AZ of that VPC. AZ A talking to AZ B is ordinary private traffic.<aside> 💭
Back to the analogy: Availability Zones are districts of the city, and your VPC is a set of buildings you own across those districts. The private post office you were given (the local route) delivers between all of your buildings, whichever district they are in. What it will never do on its own is deliver to an address outside your buildings.
</aside>
hyf-week4-vpc).hyf-week4-private-subnet-b.eu-central-1b.10.0.3.0/24.
This is the part that makes this subnet more isolated than the previous one. hyf-week4-private-subnet-a has a 0.0.0.0/0 route to the NAT Gateway, so hyf-app can reach the whole internet outbound. The database does not need that, so we will not give it that.
hyf-week4-private-rt-b, in your VPC.local route AWS creates for you.hyf-week4-private-subnet-b.
<aside> 💭
Read that route table out loud: "traffic to my own VPC goes to my own VPC; everything else has nowhere to go." A machine in this subnet can be reached by any other machine in the VPC, and can reply — but it cannot start a conversation with anything on the internet, and nothing on the internet can address it. This is the strongest isolation you can get while still being on the network.
</aside>
hyf-db, selecting hyf-week4-private-subnet-b in Network settings.hyf-db-sg with a single temporary rule: SSH (22) from 10.0.0.0/16, so you can reach it through the bastion. We tighten it in Step 3.AmazonS3FullAccess, name it hyf-db-role. (A real system would scope that policy to a single bucket; here we want permissions to stop being an interesting variable.)<aside> 💭
An IAM role on an instance is how a machine gets AWS credentials without anyone ever putting a password on it: the instance asks a link-local address for temporary keys, which rotate by themselves. That answers "who is allowed to call this service". Everything else on this page answers "how does the request physically get there". Two different questions — and mixing them up is the most common reason a failing aws command takes an hour to diagnose.
</aside>
You also need somewhere to put a backup. If you don't have one already, create an S3 bucket in eu-central-1 (S3 console → Create bucket, defaults are fine, the name has to be globally unique).

hyf-bastion sits in the public subnet in AZ A. hyf-db sits in a private subnet in AZ B whose route table has no way out at all. And yet this works, first time:
ssh -i key.pem -J ec2-user@<bastion-public-ip> ec2-user@<db-private-ip>
Take a second to see why. The packet from the bastion to 10.0.3.x matches the local route, and local spans every subnet in every AZ of the VPC. Administering this machine needs no internet, no gateway and no public IP, because the bastion is inside the VPC — it is on the same private network as everything else you have built.
That is the argument for a bastion in one sentence: it still works for a subnet that has no route to the outside world at all.
A database nobody can administer is useless. So is one that cannot take a backup. From your shell on hyf-db:
echo "pretend this is a database dump" > /tmp/backup.sql
aws s3 cp /tmp/backup.sql s3://<your-bucket-name>/ --region eu-central-1
It hangs for a while, then fails with a connection timeout. So does curl https://example.com. Nothing addressed outside 10.0.0.0/16 has anywhere to go — exactly what you designed one step ago, and now exactly what is in your way.
<aside> 💡
If instead you get Unable to locate credentials straight away, that is a different failure: the IAM role is not attached to the instance. Fix that first — we want to watch a network failure, not a permissions one. Slow failure = network. Instant failure = credentials or permissions.
</aside>
You have three options:
0.0.0.0/0 → NAT route to hyf-week4-private-rt-b — which throws away the isolation you just built, and pays a NAT Gateway by the hour to carry your backups out of the VPC and straight back into AWS.A VPC endpoint is a private connection from your VPC straight to a supported AWS service, without the traffic ever touching the public internet — and without needing a NAT Gateway, an Internet Gateway, or a public IP anywhere in the path.
There are two kinds:
| Gateway endpoint | Interface endpoint | |
|---|---|---|
| Works for | S3 and DynamoDB only | Most AWS services, including Systems Manager |
| How it appears | A target in a route table | A network interface (ENI) with a private IP inside your subnet |
| Cost | Free | Small hourly fee + data processing |
| Secured by | Route table + endpoint policy | Security group on the endpoint itself |
They achieve the same thing by completely different means, and being precise about which is which saves a lot of confused debugging later:
s3.eu-central-1.amazonaws.com still resolves to a public address — but a packet sent to that address is now handed to the endpoint instead of hunting for a gateway.secretsmanager.eu-central-1.amazonaws.com resolve to that private IP instead of a public one. From the instance's point of view the service becomes simply another machine on the local network, which is also why it needs a security group of its own.We use the gateway endpoint here: S3 is what a database actually needs, and it costs nothing.
hyf-week4-s3-endpoint.s3 and select com.amazonaws.eu-central-1.s3 — the one whose Type column says Gateway. (There is an Interface entry with an almost identical name; that column is the only thing telling them apart.)hyf-week4-vpc.hyf-week4-private-rt-b. That is the entire configuration — a gateway endpoint has no subnet and no security group. It belongs to the route tables you tick here.Now go back to Route tables → hyf-week4-private-rt-b → Routes. There is a line there you did not write: destination pl-xxxxxxxx (a prefix list), target vpce-xxxxxxxx. Your "no way out" route table now has exactly one way out, and it leads to S3 and nowhere else.
Back in your shell on hyf-db (through the bastion), run the same commands:
aws s3 cp /tmp/backup.sql s3://<your-bucket-name>/ --region eu-central-1 → works, over the endpoint.curl https://example.com → still fails. There is still no route to the internet. Good.