Architect a Secure VPC on AWS

Build a two-tier AWS network with public and private subnets.

Introduction

30 Second Summary

When you visit a website, your request passes through layers of network rules that decide whether you get in. Behind the scenes, architects design these networks so public services stay reachable while sensitive systems remain completely hidden.

In this project, you will build a secure network architecture on AWS from scratch. You will create a custom VPC (Virtual Private Cloud) with public and private subnets, launch a web server, and prove that your private subnet blocks all direct internet access.

What You'll Build

You will type a public IP address into your browser and see your web server respond, knowing that every packet traveled through infrastructure you designed and secured yourself.

By the end of this project, you'll have:

  • A custom VPC with public and private subnets where you control exactly which traffic flows in and out.
  • A live web server you can visit in your browser, accessible only because your Internet Gateway, route table, and Security Group all permit it.
  • A private subnet that provably blocks all direct internet access, demonstrating the network isolation enterprises use to protect sensitive systems.
  • Secret Mission: Configure a NAT Gateway for controlled outbound-only internet access from your private subnet.

Are there any prerequisites?

No prerequisites needed. This guide walks you through setting up everything from scratch, including your AWS account and command-line tools.

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters. This is your chance to prove you understand the foundational cloud networking skills a Solution Architect uses daily.

Set Up Your AWS Account and CLI

To build anything on AWS, you first need two things: an account that gives you access to AWS services, and a way to control those services from your Terminal.

In this step, you will create a brand new AWS account on the Free plan and install the AWS CLI on your Mac. By the end, you will be able to run AWS commands directly from Terminal.

In this step, get ready to:
  • Create a new AWS account on the Free plan.
  • Install and configure the AWS CLI on your Mac.
  • Sign into the AWS Management Console and confirm access to the EC2 Dashboard.
Create your AWS account

AWS requires an account before you can create any cloud resources. New accounts receive $100 in credit, which is more than enough for this project.

  • Open your browser and go to the AWS signup page:
  • https://signin.aws.amazon.com/signup?request_type=register
  • Enter a new email address you have not used with AWS before.
  • Create a password (8 to 128 characters, with at least 3 character types).
  • Fill in your contact information on the next screen.
  • Enter a valid credit or debit card. You will not be charged for this project.
  • Complete phone verification via SMS or voice call.
  • When prompted to choose a plan, select Free.

What is the AWS Free plan?

New AWS accounts on the Free plan receive $100 in credit at signup. The plan lasts 6 months or until credits are exhausted, whichever comes first. This project uses well under $1 in resources.

  • Wait for your account to activate. This typically takes a few minutes, but can take up to 24 hours in rare cases.
  • Check your email for a confirmation message from AWS saying your account is ready.

Account not activating?

If your account is still pending after a few minutes, check your spam folder for the confirmation email. Most accounts activate within 5 minutes. If yours takes longer, you can proceed to installing the CLI below and come back to the console step once activation completes.

Why isn't my AWS account activating?

Install the AWS CLI on your Mac

The AWS CLI lets you create and manage AWS resources directly from Terminal. You will use it throughout this project to verify your infrastructure.

  • Press Cmd+Space to open Spotlight Search.
  • Type Terminal and press Enter to open it.
  • Install the AWS CLI by running this command:
curl -fsSL https://awscli.amazonaws.com/v2/install.sh | bash

What does this command do?

This downloads and runs the official AWS install script. It installs the AWS CLI to your home directory and creates a symlink so you can run aws commands from Terminal.

The script may take 30 to 60 seconds to complete. You will see progress output as it downloads and installs.

  • Close and reopen Terminal so your shell picks up the new path.
  • Verify the installation by running:
aws --version

✔️ I see aws-cli/2.x.x in the output

The AWS CLI is installed and ready. You should see output like aws-cli/2.27.41 Python/3.11.6 Darwin/23.3.0 (your exact numbers may differ). Move on to the next part.

ⓧ I see aws-cli/1.x.x

You have an older version of the AWS CLI installed. You need version 2 for this project.

  • Run the install script again. It will upgrade your existing installation:
curl -fsSL https://awscli.amazonaws.com/v2/install.sh | bash
  • Close and reopen Terminal, then run aws --version again to confirm it now shows version 2.x.

ⓧ Command not found

Your shell cannot find the aws command. This usually means the install path is not in your shell's PATH.

  • Try running this command to add the path:
export PATH="$HOME/.local/bin:$PATH"
  • Run aws --version again.
  • If it works, make the change permanent by adding that line to your shell config:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
  • Close and reopen Terminal one more time, then confirm aws --version returns version 2.x.
Create an IAM user and configure the CLI

The CLI needs credentials to talk to your AWS account. You will create an IAM user with an access key, then feed those credentials to the CLI.

  • Sign into the AWS Management Console at https://console.aws.amazon.com using your root email and password.
  • In the search bar at the top, type IAM and select IAM from the results.
  • In the left navigation pane, click Users.
  • Click Create user.
  • Enter a user name (for example, admin-cli) and click Next.
  • For Permission options, select Attach policies directly.
  • Search for AdministratorAccess and check the box next to it.
  • Click Next, then click Create user.

Now create an access key for this user.

  • Click on the user name you just created (admin-cli) to open the user details page.
  • Select the Security credentials tab.
  • Scroll down to Access keys and click Create access key.
  • Select Command Line Interface (CLI) as the use case.
  • Check the confirmation box at the bottom, then click Next.
  • Click Create access key.
  • Click Download .csv file to save your credentials. Store this file somewhere safe. You will not be able to see the secret key again after closing this page.
  • Click Done.

Keep your credentials safe

Your access key and secret key grant full access to your AWS account. Never share them, commit them to code, or paste them into a chat. The .csv file you downloaded is the only record of the secret key.

Now configure the CLI with your new credentials.

  • Switch back to Terminal and run:
aws configure
  • When prompted, enter your AWS Access Key ID from the .csv file you downloaded.
  • Enter your AWS Secret Access Key from the same file.
  • For Default region name, enter us-east-1.
  • For Default output format, enter json.

What does aws configure do?

This command saves your credentials and preferences to a file at ~/.aws/credentials on your Mac. Every time you run an aws command after this, the CLI uses these saved credentials to authenticate your requests.

  • Confirm the CLI is working by running:
aws sts get-caller-identity

You should see a JSON response containing your account ID, user ARN, and the user name you created. This confirms the CLI can successfully authenticate with your AWS account.

Seeing an error about invalid credentials?

Double check that you pasted the access key ID and secret key correctly. Open the .csv file you downloaded and compare the values character by character. If needed, run aws configure again to re-enter them.

Help me fix my AWS CLI credentials error

Confirm access to the EC2 Dashboard

The final check is confirming you can navigate the AWS Console, since you will use it to build your VPC infrastructure in the next steps.

  • Return to the AWS Management Console in your browser.
  • In the search bar at the top, type EC2 and select EC2 from the results.
  • Confirm you can see the EC2 Dashboard showing resource counts (instances, volumes, security groups). All counts should be 0 for a new account.

Before you run your final verification in Terminal, what do you expect aws --version to output?

  • Run the version check one more time in Terminal to confirm everything is solid:
aws --version

You should see output starting with aws-cli/2. followed by version details. This confirms your CLI is installed, configured, and ready to build infrastructure.

Your environment is ready. Next up, you will create a custom VPC with public and private subnets to form the foundation of your secure network architecture.

Create Your Custom VPC and Subnets

Your AWS account is ready and the CLI is configured. Now it is time to build the network your infrastructure will live inside.

AWS gives every account a default VPC, but it is configured for convenience, not security. Building your own VPC from scratch teaches you how network isolation actually works. This is the core skill for any infrastructure architect.

In this step, get ready to:
  • Create a custom VPC with a defined IP address range.
  • Create a public subnet and a private subnet inside that VPC.
  • Enable auto-assign public IP on the public subnet.
Create your custom VPC

A VPC is your own isolated network in the cloud. You define the IP address range it covers using a CIDR block. You will use 10.0.0.0/16, which gives you 65,536 private IP addresses to work with.

  • In the AWS Console, type VPC in the search bar at the top and select **VPC** from the results.
  • In the left navigation pane, click Your VPCs.
  • Click Create VPC.
  • For Resources to create, select VPC only.
  • In the Name tag field, enter my-secure-vpc.
  • For IPv4 CIDR block, choose IPv4 CIDR manual input and enter 10.0.0.0/16.
  • Leave IPv6 CIDR block set to No IPv6 CIDR block.
  • Leave Tenancy set to Default.
  • Click Create VPC.

You should see a green banner confirming the VPC was created. The details page shows your VPC with the state Available.

What is a CIDR block?

CIDR stands for Classless Inter-Domain Routing. It is a way of defining a range of IP addresses. The number after the slash tells you how many addresses are in the range.

A /16 block gives you 65,536 addresses. A /24 block gives you 256 addresses. You are carving your VPC into smaller subnets using these ranges.

Don't see the Create VPC button?

Make sure you are in the Your VPCs section of the VPC console, not the EC2 Dashboard. The VPC console is a separate service. Also confirm you are in your intended region (top-right corner of the console).

help me find the Create VPC button in the AWS Console

Create a public and private subnet

Subnets are smaller IP ranges inside your VPC where you actually place resources like servers. You will create two: a public subnet for resources that need internet access, and a private subnet for resources that should stay hidden.

  • In the left navigation pane, click Subnets.
  • Click Create subnet.
  • For VPC ID, select your my-secure-vpc from the dropdown.

Under Subnet settings, configure the first subnet:

  • For Subnet name, enter my-public-subnet.
  • For Availability Zone, choose the first option in the list (for example, us-east-1a). Note which one you pick.
  • For IPv4 subnet CIDR block, enter 10.0.1.0/24.

Now add the second subnet on the same page:

  • Click Add new subnet.
  • For Subnet name, enter my-private-subnet.
  • For Availability Zone, choose the same Availability Zone you selected for the public subnet.
  • For IPv4 subnet CIDR block, enter 10.0.2.0/24.
  • Click Create subnet.

You should see a success banner. Both subnets now appear in the Subnets list with state Available.

Why the same Availability Zone?

Placing both subnets in the same Availability Zone keeps this project simple and avoids cross-AZ data transfer charges. In a production deployment, you would spread subnets across multiple AZs for fault tolerance.

Seeing a CIDR overlap error?

This means another subnet in the same VPC already uses an overlapping IP range. Confirm you selected my-secure-vpc (not the default VPC) in the VPC ID dropdown. The default VPC already has subnets that may conflict.

help me fix a CIDR overlap error when creating subnets

Enable auto-assign public IP on the public subnet

By default, instances launched in a custom subnet do not receive a public IP address. You need to enable this on the public subnet so any server you place there is automatically reachable from the internet.

  • In the Subnets list, select the checkbox next to my-public-subnet.
  • Click Actions, then click Edit subnet settings.
  • Under Auto-assign IP settings, check the box labeled Enable auto-assign public IPv4 address.
  • Click Save.

You should see a confirmation that the subnet settings were updated.

Why not enable this on the private subnet?

The whole point of a private subnet is that its resources are hidden from the internet. Assigning public IPs there would defeat the purpose. Only the public subnet gets this setting.

Before you move on, confirm everything is in place. What do you expect to see in the VPC console if you click Your VPCs and then check the Subnets section?

  • Click Your VPCs in the left navigation pane.
  • Confirm my-secure-vpc shows status Available with CIDR 10.0.0.0/16.
  • Click Subnets in the left navigation pane.
  • Confirm both my-public-subnet and my-private-subnet are listed under your VPC.

Your custom VPC is live with two subnets. Right now both subnets are isolated from the internet. In the next step, you will attach an Internet Gateway and configure routing so your public subnet can reach the outside world.

Connect Your VPC to the Internet

Your VPC exists, but it is completely cut off from the outside world. Right now, nothing inside it can send or receive traffic from the internet.

To fix this, you need two things: an Internet Gateway (the physical door to the internet) and a Route Table (the set of directions that tells traffic where to go). This is the same problem you would solve for a real client asking you to connect their private cloud network to the internet.

In this step, get ready to:
  • Create an Internet Gateway and attach it to your VPC.
  • Create a custom route table that sends internet traffic through the gateway.
  • Confirm your private subnet remains isolated from the internet.
Create an Internet Gateway and attach it to your VPC

An Internet Gateway is the component that allows communication between your VPC and the public internet. Without it, your subnets are a sealed box.

  • In the VPC Console left navigation pane, click Internet gateways.
  • Click Create internet gateway.
  • In the Name tag field, enter my-igw.
  • Click Create internet gateway.

You should see a green success banner at the top of the page confirming your Internet Gateway was created.

  • In the green banner at the top of the screen, click Attach to a VPC.
  • In the Available VPCs dropdown, select my-secure-vpc.
  • Click Attach internet gateway.

The state of your Internet Gateway changes to Attached. Your VPC now has a door to the internet, but no traffic is flowing yet because no route table points to it.

What is an Internet Gateway?

An Internet Gateway is a horizontally scaled, redundant VPC component that handles all traffic between your VPC and the public internet. It supports both IPv4 and IPv6 and does not create bandwidth bottlenecks.

Attaching it to your VPC makes internet access possible, but does not automatically expose anything. You still need a route directing traffic to it and a public IP on the instance.

Don't see your VPC in the dropdown?

Make sure you are in the same AWS Region where you created my-secure-vpc. Check the Region selector in the top-right corner of the Console.

help me attach my Internet Gateway to the correct VPC

Create a route table and direct internet traffic to the gateway

A route table is a set of rules that determines where network traffic goes. You will create a custom route table that sends all internet-bound traffic to your new Internet Gateway, then associate it with only your public subnet.

  • In the left navigation pane, click Route tables.
  • Click Create route table.
  • In the Name field, enter my-public-rt.
  • In the VPC dropdown, select my-secure-vpc.
  • Click Create route table.

Your new route table appears with a single local route (10.0.0.0/16 targeting local). This local route lets resources inside the VPC talk to each other, but there is no path to the internet yet.

  • With my-public-rt still selected, click Actions, then Edit routes.
  • Click Add route.
  • In the Destination field, enter 0.0.0.0/0.
  • In the Target dropdown, select Internet Gateway, then select my-igw.
  • Click Save changes.

What does 0.0.0.0/0 mean?

The destination 0.0.0.0/0 is a catch-all that matches all IPv4 addresses. It means "any traffic heading to an IP address not covered by a more specific route should go to this target." Since the only other route is the local route for 10.0.0.0/16, all non-VPC traffic gets sent to the Internet Gateway.

Now associate this route table with your public subnet so that the subnet uses these routes.

  • Click the Subnet associations tab.
  • Click Edit subnet associations.
  • Select the checkbox next to my-public-subnet.
  • Click Save associations.

Your public subnet is now explicitly associated with my-public-rt. Any instance launched in this subnet can reach the internet through the Internet Gateway.

Don't see your subnet in the association list?

Make sure you selected the route table that belongs to my-secure-vpc. Only subnets in the same VPC appear in the list.

help me associate my route table with the correct subnet

Confirm your private subnet remains isolated

Your private subnet should still use the VPC's main route table, which has no route to the Internet Gateway. This is what keeps it isolated.

  • In the left navigation pane, click Route tables.
  • Look at the list of route tables and find the one with Yes in the Main column that belongs to my-secure-vpc.
  • Click on that main route table to select it.
  • Click the Routes tab.

Before you check, predict what you expect to see. Does the main route table have a route to 0.0.0.0/0?

You should see only one route: destination 10.0.0.0/16 with target local. There is no 0.0.0.0/0 route. This confirms the private subnet has no path to the internet.

  • Click the Subnet associations tab to confirm my-private-subnet appears under Subnets without explicit associations.

This means my-private-subnet is implicitly associated with the main route table. Since the main route table has no internet route, the private subnet is completely isolated from the outside world.

Why does implicit association matter?

When you create a VPC, AWS creates a main route table automatically. Any subnet that is not explicitly associated with a custom route table uses the main route table by default. This is why your private subnet stays isolated without you needing to do anything extra.

A best practice in production is to leave the main route table restrictive (no internet route) and only grant internet access to specific subnets through custom route tables. This is exactly the pattern you just built.

Your network now has a clear separation. The public subnet can reach the internet through the Internet Gateway, while the private subnet remains a sealed environment. Next up, you will prove this architecture works by launching a web server and accessing it from your browser.

Launch a Web Server and Open It to the World

You have a fully wired network sitting idle. Routes exist, subnets are carved, and the Internet Gateway is waiting. None of it matters until traffic actually flows through it.

In this step, you will prove your architecture works by launching a real web server and visiting it in your browser. This is the moment your infrastructure comes alive.

In this step, get ready to:
  • Create a Security Group that acts as a firewall for your web server.
  • Launch an EC2 instance running a web server in your public subnet.
  • Visit your web server in a browser using its public IP address.
Create a Security Group for your web server

Before launching a server, you need to define what traffic is allowed to reach it. A Security Group is a virtual firewall that controls inbound and outbound traffic for your instance.

You will create one that allows HTTP traffic from anywhere (so the public can see your web page) and SSH traffic from your IP only (so only you can log in).

  • In the AWS Management Console, type EC2 in the top search bar and select EC2.
  • In the left navigation pane, click Security Groups.
  • Click Create security group.
  • Enter my-web-sg for Security group name.
  • Enter Allow HTTP and SSH for Description.
  • In the VPC dropdown, select my-secure-vpc.

Now add two inbound rules so the right traffic can reach your instance.

  • Under Inbound rules, click Add rule.
  • Set Type to HTTP. This automatically fills port 80.
  • Set Source to Anywhere-IPv4 (0.0.0.0/0). This lets anyone on the internet view your web page.
  • Click Add rule again to add a second rule.
  • Set Type to SSH. This automatically fills port 22.
  • Set Source to My IP. This restricts SSH access to your current computer only.
  • Leave the outbound rules at their default (allow all traffic).
  • Click Create security group.

You should see a success banner confirming your security group was created.

What is a Security Group?

A Security Group is a stateful firewall for your EC2 instances. "Stateful" means if you allow traffic in, the response is automatically allowed back out. You never need to write a matching outbound rule for responses.

By restricting SSH to your IP only, you prevent anyone else on the internet from attempting to log into your server. HTTP is left open to the public because the entire point of a web server is to serve web pages to visitors.

Don't see my-secure-vpc in the VPC dropdown?

Make sure you are in the same AWS Region where you created your VPC. Check the Region selector in the top-right corner of the console.

help me find my VPC when creating a Security Group

Launch an EC2 instance with a User Data script

With the firewall in place, you can now launch a virtual server. EC2 (Elastic Compute Cloud) gives you a resizable compute instance in the cloud. You will use a User Data script to automatically install and start a web server the moment the instance boots.

  • In the left navigation pane, click Instances.
  • Click Launch instance.

The launch instance wizard opens as a single page with several sections.

  • Under Name and tags, enter my-web-server in the Name field.
  • Under Application and OS Images (Amazon Machine Image), click Quick Start and select Amazon Linux.
  • In the Amazon Machine Image (AMI) dropdown, choose the Amazon Linux 2023 AMI (it should already be selected as the default).
  • Under Instance type, select t3.micro. This is covered by your Free Tier credits.
  • Under Key pair (login), choose Proceed without a key pair (not recommended). You will connect via SSH in the next step using EC2 Instance Connect instead.

Why no key pair?

EC2 Instance Connect lets you SSH into your instance directly from the browser without managing key files. This keeps the project simpler. In production, you would use a key pair for automated access.

Now configure the network settings so your instance lands in the correct subnet with the correct firewall.

  • Under Network settings, click Edit.
  • Set VPC to my-secure-vpc.
  • Set Subnet to my-public-subnet.
  • Confirm that Auto-assign public IP is set to Enable. This ensures your instance gets a public address.
  • Under Firewall (security groups), select Select existing security group.
  • Choose my-web-sg from the dropdown.

Finally, add the User Data script that will install the web server automatically when the instance starts.

  • Scroll down and expand Advanced details.
  • Scroll to the bottom of that section and find the User data text box.
  • Paste the following script into the User data box:
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "Hello from my secure VPC!" > /var/www/html/index.html

What does this script do?

  • yum update -y updates all installed packages to their latest versions.
  • yum install -y httpd installs Apache, a widely used web server.
  • systemctl start httpd starts the web server immediately.
  • systemctl enable httpd ensures the web server restarts automatically if the instance reboots.
  • The final line writes your custom message into the default web page file.
  • In the Summary panel on the right, review your settings.
  • Click Launch instance.

You should see a success message. Click View all instances to return to the Instances list.

  • Wait for the Instance state column to show Running and the Status check column to show 2/2 checks passed. This may take 1-2 minutes. Click the refresh icon if needed.

Instance stuck in Pending or went to Terminated?

If your instance goes straight to Terminated, check that you selected my-public-subnet (not the private subnet) and my-secure-vpc (not the default VPC). Also confirm that the instance type is t3.micro which is eligible for your Free Tier credits.

help me troubleshoot why my EC2 instance went to Terminated immediately after launch

Visit your web server in the browser

Your instance is running. The User Data script has installed Apache and written your custom web page. Time to prove the whole architecture works end-to-end.

  • Click on your my-web-server instance to open its details.
  • In the Details tab, find the Public IPv4 address field and copy the IP address.

Before you paste it into your browser, think about what you expect to see. Will the page load successfully, or will it time out?

  • Open a new browser tab.
  • Paste the public IP address into the address bar and press Enter.

You should see a page displaying the text: Hello from my secure VPC!

How did traffic reach your instance?

Your browser sent an HTTP request to the public IP. The Internet Gateway forwarded it into your VPC. The route table in your public subnet directed it to the instance. The Security Group checked that port 80 was allowed from your IP (it is, from anywhere). Apache received the request and served the page back through the same path.

Page not loading or connection timed out?

  • Make sure you are using http:// and not https:// in your browser. Your Security Group only allows port 80 (HTTP), not port 443 (HTTPS).
  • Confirm the instance shows 2/2 checks passed in the Status check column. If it still says Initializing, the User Data script is still running. Wait another minute.
  • Verify that my-web-sg has an inbound rule for HTTP on port 80 from 0.0.0.0/0. Go back to Security Groups and check the Inbound rules tab.

my web page isn't loading when I visit the public IP address of my EC2 instance

Traffic is flowing. Your browser made it through the Internet Gateway, past the route table, through the Security Group, and to your web server. You now have a live, publicly accessible server running inside a custom VPC you built from scratch.

Next up, you will prove the other side of the coin. Your private subnet should block all direct internet access. Time to verify that isolation actually works.

Verify Network Isolation

In the previous step, you launched a web server and proved traffic can flow from the internet into your public subnet. That is only half the story.

A Solution Architect must prove that security controls actually work, not just assume they do. You will now launch a second instance in the private subnet and demonstrate that it cannot be reached from the internet, while still communicating internally with resources inside the same VPC.

In this step, get ready to:
  • Launch a private instance with a Security Group that only allows internal VPC traffic.
  • SSH into your public instance and ping the private instance to confirm internal connectivity.
  • Prove the private instance has no public IP and cannot be reached from the internet.
Create a Security Group for the private instance

Your private instance needs its own firewall rules. This Security Group will allow traffic only from IP addresses within your VPC (10.0.0.0/16), blocking everything from the outside world.

  • In the AWS Management Console, open the VPC service.
  • In the left navigation pane, click Security Groups.
  • Click Create security group.
  • For Security group name, enter my-private-sg.
  • For Description, enter Allow traffic from within VPC only.
  • For VPC, select my-secure-vpc from the dropdown.

Now add the inbound rule that restricts access to internal VPC traffic only:

  • In the Inbound rules section, click Add rule.
  • For Type, select All traffic.
  • For Source, select Custom and enter 10.0.0.0/16.
  • Leave the outbound rules as the default (all traffic allowed).
  • Click Create security group.

You should see a green success banner confirming the security group was created.

Why allow all traffic from 10.0.0.0/16?

The CIDR block 10.0.0.0/16 is your entire VPC's address range. Any instance inside your VPC has a private IP that falls within this range.

By setting the source to this CIDR, you allow communication from any resource in your VPC while blocking everything from outside it. This is the standard pattern for private-tier resources like databases and internal APIs.

Launch an instance in the private subnet

With the security group ready, you can launch a second EC2 instance into the private subnet. This instance will have no public IP, making it invisible to the internet.

  • Navigate to the EC2 service in the console.
  • Click Launch instances.
  • For Name, enter my-private-server.
  • Under Application and OS Images, select Amazon Linux 2023 (it should already be selected as the default).
  • Under Instance type, select t3.micro.
  • Under Key pair, select the same key pair you used for my-web-server.

Now configure the network settings to place this instance in the private subnet:

  • Click Edit next to Network settings.
  • For VPC, select my-secure-vpc.
  • For Subnet, select my-private-subnet.
  • For Auto-assign public IP, confirm it shows Disable.
  • Under Firewall (security groups), select Select existing security group.
  • Choose my-private-sg from the list.
  • Click Launch instance.

Wait for the instance state to show Running in the Instances list. This takes about 30 seconds.

  • Click on my-private-server in the instances list to open its details.
  • Note down the Private IPv4 address (it will be something like 10.0.2.x).
  • Confirm the Public IPv4 address field shows a dash (—) or is empty.

Why is there no public IP?

You placed this instance in my-private-subnet, which has auto-assign public IP disabled. Without a public IP, no one on the internet can address this instance directly.

Even if someone guessed the private IP (10.0.2.x), private IPs are not routable on the public internet. The instance is effectively invisible to the outside world.

Instance stuck in Pending?

Give it up to 60 seconds. Click the refresh icon in the instances list to update the status. If the instance moves to Terminated instead of Running, check that you selected t3.micro as the instance type and that my-private-subnet is in the same Availability Zone as my-public-subnet.

Still stuck? Help me troubleshoot my EC2 instance that won't start in the private subnet.

SSH into the public instance and test internal connectivity

Now for the real test. You will SSH into your public instance and attempt to reach the private instance using its private IP. If your network architecture is correct, traffic should flow between the two instances inside the VPC.

  • Open Terminal on your Mac.
  • Set the correct permissions on your key file by running this command:
chmod 400 ~/Downloads/[[KEYPAIR_FILE="your-key-pair-file.pem"]]
  • SSH into your public instance by running this command:
ssh -i ~/Downloads/[[KEYPAIR_FILE="your-key-pair-file.pem"]] ec2-user@[[WEB_SERVER_PUBLIC_IP="your-web-server-public-IP"]]

What does this command do?

The ssh command opens a secure connection to a remote machine. The -i flag specifies the private key file that proves your identity. The ec2-user is the default username on Amazon Linux instances.

You may see a fingerprint warning asking if you want to continue connecting. Type yes and press Enter.

Your terminal prompt changes to something like [ec2-user@ip-10-0-1-x ~]$, confirming you are now inside the public instance.

Before you run the next command, make a prediction. Do you think your public instance can reach the private instance, even though the private instance has no internet access?

  • From inside the public instance, ping the private instance by running this command (replace the IP with the private IP you noted earlier):
ping -c 4 [[PRIVATE_INSTANCE_IP="10.0.2.x (your private instance private IP)"]]

You should see 4 successful replies, similar to this:

PING 10.0.2.x (10.0.2.x) 56(84) bytes of data.
64 bytes from 10.0.2.x: icmp_seq=1 ttl=127 time=0.5 ms
64 bytes from 10.0.2.x: icmp_seq=2 ttl=127 time=0.4 ms
64 bytes from 10.0.2.x: icmp_seq=3 ttl=127 time=0.4 ms
64 bytes from 10.0.2.x: icmp_seq=4 ttl=127 time=0.4 ms

--- 10.0.2.x ping statistics ---
4 packets transmitted, 4 received, 0% packet loss

Why does this work?

Both instances live inside the same VPC (10.0.0.0/16). Traffic between instances in the same VPC stays within AWS's internal network and never touches the internet.

The private instance's security group (my-private-sg) allows all traffic from 10.0.0.0/16, which includes your public instance's private IP. The ping succeeds because it is internal VPC communication, not internet traffic.

Ping showing 100% packet loss?

Check that you entered the correct private IP for my-private-server (starts with 10.0.2.).

Verify that my-private-sg has an inbound rule with type All traffic and source 10.0.0.0/16. Also confirm both subnets are in the same Availability Zone.

Need more help? Help me debug why my ping to the private instance is failing.

Confirm the private instance is unreachable from the internet

You have proven internal connectivity works. The final piece is confirming that no one outside your VPC can reach this instance.

  • Type exit and press Enter to leave the SSH session and return to your local terminal.
  • Return to the EC2 console in your browser.
  • Click on my-private-server in the instances list.
  • In the Details tab, look at the Public IPv4 address field.

The field is empty. Without a public IP, there is no address anyone on the internet can use to reach this instance. Combined with the private subnet's route table (which has no route to the Internet Gateway), this instance is fully isolated from the public internet.

Compare this with my-web-server:

  • Click on my-web-server in the instances list.
  • Notice it has a Public IPv4 address and is in my-public-subnet which routes to the Internet Gateway.

Two layers of isolation

Your private instance is protected by two independent controls. First, it has no public IP address, so there is nothing for the internet to target.

Second, even if it somehow received a public IP, the private subnet's route table has no route to the Internet Gateway. Traffic would have nowhere to go. This defense-in-depth approach is what enterprise architectures rely on to protect sensitive resources like databases.

You have successfully validated your network architecture. The public subnet allows internet traffic to reach your web server, while the private subnet keeps your internal instance completely hidden from the outside world.

Secret mission

Add a NAT Gateway for Controlled Outbound Access

Your private subnet is completely isolated from the internet. But what if those private instances need to download software updates or patches? In this secret mission, you will configure a NAT Gateway to give your private instance controlled outbound internet access while keeping it unreachable from the outside world.

Clean Up Your Resources

Clean Up Your Resources

This project uses AWS cloud resources, some of which have ongoing costs if left running. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Cost warning

If you completed the Secret Mission, your NAT Gateway costs approximately $0.045/hour plus data transfer charges. Your Elastic IP is free while associated with the running NAT Gateway, but costs $0.005/hour if the NAT Gateway is deleted without releasing the IP.

Delete the NAT Gateway and release the Elastic IP immediately after testing to stay within your Free Tier credits.

Resources you used:

  • EC2 instances: my-web-server and my-private-server (covered by your $100 signup credit).
  • NAT Gateway: my-nat-gw (costs ~$0.045/hour, Secret Mission only).
  • Elastic IP address associated with the NAT Gateway.
  • VPC: my-secure-vpc with subnets, route tables, Internet Gateway, and Security Groups (no ongoing cost).

Keep everything running

No action needed on most resources. Choose this if you plan to experiment further within your 6-month Free Tier window. Your EC2 instances are covered by your $100 signup credit, and VPC resources (subnets, route tables, Internet Gateway, Security Groups) have no ongoing cost.

However, you should still delete the NAT Gateway and release the Elastic IP to avoid charges.

  • In the AWS Console, go to VPC then NAT gateways.
  • Select my-nat-gw, click Actions, then Delete NAT gateway.
  • Type delete in the confirmation field and confirm.
  • Wait a few minutes for the NAT Gateway status to change to Deleted.
  • Go to EC2 then Elastic IPs.
  • Select your Elastic IP, click Actions, then Release Elastic IP addresses.
  • Confirm the release.

Pause - I'll come back to this later

Stop your EC2 instances to free up compute credits while keeping your VPC architecture intact. You will not be charged for compute while instances are stopped. EBS storage remains but is covered under your Free Tier credits.

  • Delete the NAT Gateway and release the Elastic IP using the same steps in the Keep tab above.
  • In the AWS Console, go to EC2 then Instances.
  • Select both my-web-server and my-private-server.
  • Click Instance state, then Stop instance.
  • Confirm to stop both instances.
  • Your VPC, subnets, route tables, Internet Gateway, and Security Groups remain in place at no cost. When you return, start the instances from the same menu.

Delete - I don't want to use this again

Remove all project resources to ensure $0 ongoing cost. Delete resources in this order to respect dependencies.

Step 1: Delete the NAT Gateway.

  • Go to VPC then NAT gateways.
  • Select my-nat-gw, click Actions, then Delete NAT gateway.
  • Type delete to confirm. Wait for the status to show Deleted before continuing.

Step 2: Release the Elastic IP.

  • Go to EC2 then Elastic IPs.
  • Select your Elastic IP, click Actions, then Release Elastic IP addresses. Confirm.

Step 3: Terminate both EC2 instances.

  • Go to EC2 then Instances.
  • Select both my-web-server and my-private-server.
  • Click Instance state, then Terminate instance. Confirm termination.
  • Wait for both instances to show Terminated status before continuing.

Step 4: Delete subnets.

  • Go to VPC then Subnets.
  • Select my-public-subnet, click Actions, then Delete subnet. Confirm.
  • Repeat for my-private-subnet.

Step 5: Delete the custom route table.

  • Go to VPC then Route tables.
  • Select my-public-rt, click Actions, then Delete route table. Confirm.
  • The main route table will be deleted automatically when the VPC is deleted.

Step 6: Detach and delete the Internet Gateway.

  • Go to VPC then Internet gateways.
  • Select my-igw, click Actions, then Detach from VPC. Confirm.
  • With my-igw still selected, click Actions, then Delete internet gateway. Confirm.

Step 7: Delete Security Groups.

  • Go to VPC then Security groups.
  • Select my-web-sg and my-private-sg, click Actions, then Delete security groups. Confirm.
  • The default Security Group is deleted automatically with the VPC.

Step 8: Delete the VPC.

  • Go to VPC then Your VPCs.
  • Select my-secure-vpc, click Actions, then Delete VPC. Confirm.
  • This removes the VPC and any remaining associated resources (main route table, default Security Group).

Nice Work!

Nice Work!

You just architected a production-style network on AWS from scratch. You have a working two-tier VPC that demonstrates how enterprises isolate sensitive resources from the public internet.

You've learned how to:

  • Built a custom VPC with public and private subnets, an Internet Gateway, and route tables that control exactly how traffic flows between the internet and your resources.
  • Deployed a live EC2 web server accessible from your browser, secured by a Security Group firewall that allows only the traffic you explicitly permit.
  • Proved network isolation works by demonstrating that the private subnet blocks all direct internet access, validating your architecture the way a Solution Architect would.
  • Secret Mission: Configured a NAT Gateway for controlled outbound-only internet access from the private subnet, proving your private instances can download updates without being reachable from the outside.

Ready to quiz yourself?