Week 4

VPC - An analogy about shared resources

Public subnets and Internet Gateway

Private subnets and NAT

Endpoints and Security Groups

Putting it all together

Load balancing - one front door, many clerks

Auto Scaling

Architecture and Tradeoffs

Practice

Assignment

Cloud Track

Under construction

<aside> 🚧

This page is currently under construction. Please check back later.

</aside>

<aside> 🧱

Step 2 of 3 — building the week's architecture

In the previous section you built the public subnet in Availability Zone A with hyf-web in it. This section adds the second layer, in the same Availability Zone: a private subnet holding the application server hyf-app, the bastion host you will use to log in to it, and the NAT Gateway that lets it fetch updates without ever being reachable from the internet.

Still missing after this page: the database in a second Availability Zone, and the Security Groups that decide who may talk to whom. That is section 3.

</aside>


In ‣ we made a subnet public by attaching an Internet Gateway and pointing its route table at it. Now we'll build the mirror image: a subnet that cannot be reached from the internet at all, but whose resources can still reach out when they genuinely need to — to download OS updates, pull a Docker image, call an external API — via a NAT Gateway.

This is where the architecture starts to have a shape. Ask yourself why you would bother: your web server is already online and working. The answer is that a web server is the machine most likely to be attacked, because it is the one you deliberately exposed. Everything valuable behind it — your application logic, and later your data — should live somewhere an attacker cannot reach even after breaking into the web tier. "Somewhere they cannot reach" is exactly what a private subnet is.

<aside> 💭

We deliberately put this private subnet in the same Availability Zone as the public one (eu-central-1a). Two reasons: a NAT Gateway lives in one AZ and traffic crossing AZ boundaries is billed, and keeping AZ A self-contained makes the next section's cross-AZ hop (to the database in AZ B) obvious rather than accidental.

</aside>

FullPic-Page-3.drawio.png

Step 1 Create a private subnet and an EC2 instance

Create a new subnet

Same process as before, in the same VPC, but this time we won't give it a route to the internet.

  1. In the VPC console, go to Subnets → Create subnet.
  2. Select your existing VPC (hyf-week4-vpc).
  3. Name it, e.g. hyf-week4-private-subnet-a.
  4. Pick the same Availability Zone as your public subnet — eu-central-1a. The -a suffix in the name is there to remind you which AZ it belongs to.
  5. Set an IPv4 CIDR block that doesn't overlap your public subnet, e.g. 10.0.2.0/24.
  6. Click Create subnet.

<aside> 💭

You now have two subnets in the same district (AZ) of the same building (VPC): 10.0.1.0/24 facing the street and 10.0.2.0/24 with no door to the outside. Same building, same floor plan — the only thing that will make them behave differently is the route table.

</aside>

Screenshot 2026-08-19 at 17.18.55.png

Routing: local only

If you don't explicitly associate a route table with this subnet, it falls back to the VPC's main route table — and a fresh main route table only contains the local route (traffic to your VPC's own CIDR stays inside the VPC). There is no 0.0.0.0/0 entry pointing at the Internet Gateway, so nothing addressed to the outside world has anywhere to go.

<aside> 💭

That's the entire mechanism behind "private": not a flag you tick, just the absence of a route out. Combined with not assigning a public IP, the instance is unreachable from the internet and, for now, can't reach it either.

</aside>

Launch the app server (EC2) in the private subnet

This instance is the application tier: in the final architecture it is the machine that runs your API, receives requests forwarded by hyf-web, and later queries the database. It never talks to the internet directly.

  1. Launch an instance the same way as before, name it hyf-app, but this time select the private subnet (hyf-week4-private-subnet-a) in Network settings.
  2. Leave Auto-assign public IP disabled (it may not even be available once a private subnet is selected).
  3. Create a security group named hyf-app-sg that allows inbound traffic only from within the VPC — for now, SSH (22) and your app's port (e.g. TCP 3000) from your VPC's CIDR 10.0.0.0/16, not from the whole internet.

<aside> 💭

Allowing the whole VPC CIDR is a blunt instrument — it means any machine you ever put in this VPC can knock on this door. In section 3 we replace 10.0.0.0/16 with references to hyf-web-sg (for the API port) and hyf-bastion-sg (for SSH), so only the web server can call the app server and only the bastion can log in to it — whatever their IPs happen to be.

</aside>

2026-08-19 17.25.29 eu-central-1.console.aws.amazon.com c0e198b57325.png

2026-08-19 17.25.01 eu-central-1.console.aws.amazon.com b43f22dba5a1.png

Note its private IPv4 address once it's running — there is no public one to note this time.

Step 2 Reach an instance that has no public IP

With no public IP and no route to the internet, you can't ssh into this instance directly from your laptop. The machine is running, and from where you are sitting it may as well not exist.

The course solution: a bastion host

A bastion (also called a "jump box") is a small EC2 instance that lives in the public subnet, reachable from the internet, whose only job is to be a stepping stone into your private instances. It is the single door into the private tier — which is exactly why it is the one machine you guard properly.

This is what we use for the rest of the week. Every hop stays visible: you can point at the route and the rule that made each one work, and the only new skill it needs is ssh.

  1. Launch a small instance (t3.micro is plenty) in your public subnet from the previous section. Name it hyf-bastion.
  2. Auto-assign public IP: enabled. This is the one machine on the path into your private tier that has to be reachable from outside.
  3. Create a security group named hyf-bastion-sg with exactly one inbound rule: SSH (22) from My IP — your own address, never 0.0.0.0/0.
  4. Use the same key pair as your other instances.

Then connect from your laptop, in two hops:

ssh -i key.pem ec2-user@<bastion-public-ip>   # hop 1: into the bastion
ssh ec2-user@<app-private-ip>                 # hop 2: from the bastion, into hyf-app

Or in a single command — -J ("jump") tells SSH to tunnel through the bastion for you:

ssh -i key.pem -J ec2-user@<bastion-public-ip> ec2-user@<app-private-ip>

Use ubuntu@ instead of ec2-user@ if you launched Ubuntu AMIs. The second hop is allowed because hyf-app-sg currently accepts SSH from the whole VPC CIDR, and the bastion is inside it — in section 3 we replace that with a rule naming hyf-bastion-sg specifically.

<aside> 🔑

Prefer -J over copying key.pem onto the bastion. The bastion is the most exposed machine in your VPC; a private key sitting on it is a private key an attacker gets for free the moment they get in. With -J the key never leaves your laptop — the bastion only forwards an encrypted tunnel it cannot read.

</aside>

<aside> ⚠️

Be honest about what a bastion costs you: another machine to patch and monitor, and an SSH port open to the internet somewhere in your VPC. That is why rule 3 is narrowed to your own IP, and why you stop the instance when you are not using it. Home IP addresses change — if SSH suddenly hangs one morning, check that rule before you debug anything else.

</aside>

Also worth recognising: SSM Session Manager

In production you will often meet AWS Systems Session Manager instead. It opens a shell on an instance from the console or CLI with no public IP, no bastion and no open SSH port at all: an agent on the instance calls outbound to the Systems Manager service, and you meet it there. It needs an IAM role on the instance and a network path to that service.

We stay with the bastion in this course, because the network path is the thing you are here to learn and the bastion keeps it in plain sight. Recognise the name — it is what replaces the bastion the day you stop wanting one.

Step 3 The update problem, and NAT

Say this is an Ubuntu box and you want to run apt update && apt upgrade, or docker pull an image. Both need outbound internet access — which, right now, this instance simply doesn't have: no route out, and no public IP even if it did.

A NAT Gateway solves this. It's a managed, AWS-operated proxy that sits in a public subnet (it needs its own public IP, via an Elastic IP) and forwards outbound traffic from private-subnet resources to the Internet Gateway, translating source addresses so replies find their way back. Unlike an Internet Gateway, it's one-directional: private instances can start connections out through it, but nothing on the internet can use it to start a connection in.

Create a NAT Gateway

  1. In the VPC console, go to NAT Gateways → Create NAT Gateway.
  2. Name it, e.g. hyf-week4-natgw.
  3. Subnet: choose your public subnet — not the private one, the NAT Gateway itself needs a route to the internet.
  4. Allocate a new Elastic IP (or pick an existing unused one).
  5. Click Create NAT Gateway, and wait for it to become Available (this can take a few minutes).

Screenshot 2026-08-29 at 11.11.00.png

Update the private subnet's route table

  1. In Route tables, create a new route table (e.g. hyf-week4-private-rt-a) associated with your VPC — don't reuse the public one, they need different destinations.
  2. Open its Routes tab → Edit routes → Add route: destination 0.0.0.0/0, target → your NAT Gateway.
  3. Save the routes.
  4. Open Subnet associations → Edit subnet associations → tick your private subnet.

Screenshot 2026-08-29 at 11.18.18.png

Screenshot 2026-08-29 at 11.19.17.png

Test it

Connect to hyf-app through the bastion (Step 2), then:

Sum up

Public and private subnets are built from the exact same ingredients — a subnet, a route table, and something plugged into that route table — just pointed in different directions: