Cloud Computing, AWS / April 25, 2026 / by Frank Remmy

How to Build a VPC and Launch a Web Server on AWS

This walkthrough covers building a fully functional Virtual Private Cloud (VPC) from scratch, configuring subnets across two Availability Zones, setting up a security group, and launching an EC2 instance running a live web server.

By the end of this lab, the following infrastructure will be running on AWS:

  • A custom VPC with a /16 CIDR block
  • Four subnets across two Availability Zones (two public, two private)
  • An Internet Gateway and a NAT Gateway
  • Route tables correctly associated with public and private subnets
  • A security group allowing HTTP traffic
  • An EC2 instance running Apache, deployed via a User Data script

The Target Architecture

Before touching the AWS console, it helps to understand what the finished infrastructure looks like.

Target architecture diagram

The VPC (lab-vpc: 10.0.0.0/16) spans two Availability Zones: us-east-1a and us-east-1b. Each AZ contains one public subnet and one private subnet. The Internet Gateway sits at the edge of the VPC and handles all inbound and outbound internet traffic for the public subnets. The NAT Gateway, deployed inside the public subnet of AZ-A, gives private subnet resources the ability to make outbound internet requests without being directly reachable from outside.

The route tables on the right of the diagram tell the routing story clearly:

  • Public route table: local traffic stays within the VPC (10.0.0.0/16 → local), everything else goes to the Internet Gateway (0.0.0.0/0 → Internet gateway)
  • Private route table: local traffic stays within the VPC, everything else goes through the NAT Gateway (0.0.0.0/0 → NAT gateway)

Web Server 1 will be deployed inside lab-subnet-public2 in AZ-B, wrapped in a security group that permits HTTP traffic on port 80.

Task 1: Create the VPC

Navigate to the VPC console by searching for VPC in the AWS search bar. From the VPC dashboard, choose Create VPC.

Choosing “VPC and more”

AWS presents two options: VPC only (creates just the network shell, requiring manual configuration of all other resources) or VPC and more (the wizard creates subnets, route tables, an internet gateway, and a NAT gateway in a single workflow). Select VPC and more.

Under Name tag auto-generation, keep Auto-generate selected and change the value to lab. This prefix is used to auto-name every resource the wizard creates – lab-vpc, lab-subnet-public1-us-east-1a, lab-rtb-public, and so on.

Set the IPv4 CIDR block to 10.0.0.0/16. This gives the VPC 65,536 private IP addresses to allocate across all subnets.

Configuring subnets and the NAT Gateway

Scroll down to configure the subnet settings:

Create VPC - subnet CIDRs and NAT Gateway
  • Set Number of Availability Zones to 1
  • Keep Number of public subnets at 1 and Number of private subnets at 1
  • Expand Customize subnets CIDR blocks and set:
    • Public subnet CIDR block: 10.0.0.0/24
    • Private subnet CIDR block: 10.0.1.0/24
  • Set NAT gateways to In 1 AZ
  • Set VPC endpoints to None
  • Keep both DNS hostnames and DNS resolution enabled

The NAT Gateway is deployed inside the public subnet so that instances in the private subnet can reach the internet for outbound traffic, software updates, API calls, without being directly reachable from outside.

Reviewing the live preview

As the settings are configured, the preview panel on the right builds the architecture in real time.

Create VPC - preview panel showing all resources

By this point, the preview shows three columns populated: subnets, route tables, and network connections, including lab-igw (Internet Gateway) and lab-nat-public1-us-east-1a (NAT Gateway with 1 ENI and 1 Elastic IP). All of this will be created in a single action.

Choose Create VPC.

The creation workflow

VPC creation workflow - all green ticks

AWS runs 18 sequential provisioning steps and logs each one in real time:

  1. Creates the VPC and enables DNS hostnames and DNS resolution
  2. Creates two subnets (public and private)
  3. Creates the Internet Gateway and attaches it to the VPC
  4. Creates the first route table, adds a route, and associates it with the public subnet
  5. Allocates an Elastic IP address for the NAT Gateway
  6. Creates the NAT Gateway and waits for it to become active
  7. Creates the second route table for the private subnet, adds a route pointing to the NAT Gateway, and associates it

The “Wait for NAT Gateways to activate” step is deliberately blocking. Nothing can proceed until the NAT Gateway is ready, because the private route table needs it to exist before it can reference it.

Note for production environments: The Elastic IP allocation and NAT Gateway are real billable resources. NAT Gateways are one of the more expensive VPC components. Factor this in before leaving one running outside of a lab.

Viewing the completed VPC

Choose View VPC to land on the VPC detail page.

VPC dashboard showing resource map

The Details panel confirms:

  • State: Available
  • IPv4 CIDR: 10.0.0.0/16
  • DNS hostnames and DNS resolution: enabled – EC2 instances launched in this VPC will automatically receive human-readable DNS names, which matters when connecting to the web server later
  • Default VPC: No – this is a custom VPC, not the AWS-provided default

The Resource map tab shows the live architecture: both subnets in us-east-1a, three route tables (including the VPC’s default main route table), and two network connections – the Internet Gateway and the NAT Gateway.

Task 2: Create Additional Subnets

A single Availability Zone is a single point of failure. To achieve high availability, subnets need to exist in at least two physically separate AZs. This task adds a second public subnet and a second private subnet in us-east-1b.

Creating lab-subnet-public2

In the left navigation pane, choose Subnets, then Create subnet.

Creating lab-subnet-public2

Configure the following:

  • VPC ID: lab-vpc
  • Subnet name: lab-subnet-public2
  • Availability Zone: us-east-1b
  • IPv4 CIDR block: 10.0.2.0/24

The IPv4 VPC CIDR block dropdown shows 10.0.0.0/16. This is confirming which VPC the subnet belongs to, not a field to change. The subnet CIDR (10.0.2.0/24) is a /24 block carved from within the parent /16, which is how subnets work. Choose Create subnet.

Creating lab-subnet-private2

Repeat the process for the private subnet:

Creating lab-subnet-private2
  • VPC ID: lab-vpc
  • Subnet name: lab-subnet-private2
  • Availability Zone: us-east-1b
  • IPv4 CIDR block: 10.0.3.0/24

Choose Create subnet.

Confirming all four subnets

Subnets list showing all four lab subnets

The subnets list confirms all four are Available and associated with lab-vpc:

Subnet AZ CIDR Type
lab-subnet-public1-us-east-1a us-east-1a 10.0.0.0/24 Public
lab-subnet-private1-us-east-1a us-east-1a 10.0.1.0/24 Private
lab-subnet-public2 us-east-1b 10.0.2.0/24 Public
lab-subnet-private2 us-east-1b 10.0.3.0/24 Private

The other subnets visible in the list (with 172.31.x.x CIDRs) belong to the AWS default VPC, these can be ignored.

Associating subnets with route tables

Creating the new subnets is not enough, they need to be associated with the correct route tables. Navigate to Route tables in the left navigation pane.

Private route table: Select lab-rtb-private1-us-east-1aSubnet associations tab → Edit subnet associations. Ensure both lab-subnet-private1-us-east-1a and lab-subnet-private2 are selected. Save.

Public route table: Select lab-rtb-publicSubnet associations tab → Edit subnet associations. Ensure both lab-subnet-public1-us-east-1a and lab-subnet-public2 are selected. Save.

Route tables list showing 2 subnets each

Both route tables now show 2 subnets in the Explicit subnet associations column. Public subnets route internet-bound traffic to the Internet Gateway; private subnets route it through the NAT Gateway. Both AZs now have correct routing behaviour.

Architecture checkpoint

Updated architecture diagram after Task 2

The VPC skeleton is fully in place. All four subnets are live across two Availability Zones, the Internet Gateway and NAT Gateway are deployed, and the route tables are correctly wired. The only remaining piece is the web server instance inside lab-subnet-public2, that comes after the security group is configured.

Task 3: Create a Security Group

A security group acts as a virtual firewall at the instance level. It controls what traffic is allowed to reach an EC2 instance. Navigate to Security groups in the left navigation pane under Security.

Choose Create security group and configure:

  • Security group name: Web Security Group
  • Description: Enable HTTP access
  • VPC: lab-vpc

Under Inbound rules, choose Add rule:

  • Type: HTTP
  • Source: Anywhere-IPv4 (0.0.0.0/0)
  • Description: Permit web requests

Choose Create security group.

Web Security Group created successfully

The result is a security group with one inbound rule: HTTP / TCP / Port 80 / 0.0.0.0/0. This means any IP address on the internet can send HTTP traffic to any instance this security group is attached to.

A few things worth noting:

  • Outbound rules: AWS automatically adds a default outbound rule allowing all traffic out. This is the stateful behaviour of security groups in action. If inbound HTTP traffic is allowed, the instance can send responses back without an explicit outbound rule.
  • Scope: The security group is tied to lab-vpc. It can only be attached to instances inside that VPC.
  • No SSH rule: Port 22 is intentionally absent. The web server will be configured entirely through a User Data script at launch, so no manual SSH access is needed.

Task 4: Launch the Web Server Instance

Navigate to the EC2 console by searching for EC2 in the AWS search bar. Choose Launch instances.

Configuring the instance

Name: Web Server 1

AMI: Keep the default Amazon Linux 2023 AMI selected.

Instance type: t2.micro

Note: The AWS console may default to t2.micro or t3.micro depending on region availability. Select t2.micro explicitly to match the lab requirements.

Key pair: Select vockey

Network settings – choose Edit and configure:

  • Network: lab-vpc
  • Subnet: lab-subnet-public2 (public subnet in AZ-B, not the private subnet)
  • Auto-assign public IP: Enable
  • Firewall (security groups): Select existing → Web Security Group

The User Data script

Expand Advanced details and paste the following into the User data box:

#!/bin/bash
# Install Apache Web Server and PHP
dnf install -y httpd wget php mariadb105-server
# Download Lab files
wget https://aws-tc-largeobjects.s3.us-west-2.amazonaws.com/CUR-TF-100-ACCLFO-2/2-lab2-vpc/s3/lab-app.zip
unzip lab-app.zip -d /var/www/html/
# Turn on web server
chkconfig httpd on
service httpd start

This script runs automatically with root permissions the first time the instance boots. Breaking it down:

  • dnf install -y httpd wget php mariadb105-server – installs Apache (httpd), wget for downloading files, PHP, and MariaDB silently
  • wget ...lab-app.zip – downloads the web application from an S3 bucket
  • unzip lab-app.zip -d /var/www/html/ – extracts the application into Apache’s web root, making it immediately accessible on port 80
  • chkconfig httpd on – sets Apache to start automatically on every reboot
  • service httpd start – starts Apache immediately

This is why no manual SSH or configuration is needed after launch, the infrastructure and application deploy together in a single action.

Choose Launch instance.

Waiting for status checks

EC2 instance list showing Web Server 1 running with checks passed

After launch, the instance goes through a boot and initialisation sequence. Refresh the instances list periodically until Web Server 1 shows 3/3 checks passed in the Status check column. This typically takes 2–3 minutes.

The high CPU load visible when the web page first loads (72% in this case) is normal. The instance is still completing the User Data script installation in the background.

Verifying the web server

Select Web Server 1 and copy the Public IPv4 DNS value from the Details tab. Open a new browser tab, paste the value, and press Enter.

Live web application showing instance metadata

The web application loads and displays live instance metadata:

  • InstanceId: confirms which EC2 instance is serving the request
  • Availability Zone: us-east-1b – confirms the server is running in lab-subnet-public2 as intended
  • Current CPU Load – live data pulled from the instance

What happened end to end: the browser sent an HTTP request on port 80 → the Internet Gateway received it → routed it to lab-subnet-public2 → the Web Security Group checked the inbound rule (TCP/80/0.0.0.0/0 ✓) → the request reached Apache on the EC2 instance → the response travelled back the same path. Every component built in this lab played its role.

Final Architecture

Final architecture diagram with Web Server 1 deployed

The completed infrastructure matches the target architecture from the start of the lab:

  • lab-vpc spanning two Availability Zones with a /16 CIDR
  • Four subnets correctly distributed and routed
  • Internet Gateway handling public traffic, NAT Gateway handling private outbound traffic
  • Web Server 1 live in lab-subnet-public2, protected by the Web Security Group

Key Takeaways

CIDR planning matters. The subnet addressing scheme used here (10.0.0.0/24, 10.0.1.0/24, 10.0.2.0/24, 10.0.3.0/24) is deliberate, each subnet gets its own /24 block carved from the parent /16. AWS reserves 5 IP addresses in every subnet, so a /24 gives 251 usable addresses.

Route tables define public vs private. A subnet is only “public” because its route table has a route pointing 0.0.0.0/0 to an Internet Gateway. Change that route and it becomes private. The subnet itself has no inherent public or private property.

Security groups are stateful. An inbound allow rule automatically permits the corresponding response traffic out. Network ACLs, by contrast, are stateless and require explicit rules in both directions.

User Data scripts enable automated deployments. Rather than manually configuring a server after launch, the entire application stack was installed and started through a bootstrap script. This is the foundation of infrastructure automation on AWS.

Instance type selection is exact. Lab graders and cost calculators both care about the specific instance type chosen. t2.micro and t3.micro are not interchangeable in a graded context, even though they’re similar in practice. Instance types can be changed after creation by stopping the instance, changing the type, and restarting, but public IP addresses may change as a result unless an Elastic IP is used.

Tags:
1 Comment

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from Ugochukwu Chigbata

Subscribe now to keep reading and get access to the full archive.

Continue reading