VPC Setup
VPC is your private network inside AWS. All your resources (EC2, ALB, ASG) live inside it.
Search VPC in the top bar → Click Your VPCs → Create VPC
Select VPC and more (this auto-creates subnets, route tables, and internet gateway)
Name tag: Web-VPC
IPv4 CIDR: 10.0.0.0/16
Number of AZs: 2, Public subnets: 2, Private subnets: 2
NAT Gateways: None (to avoid cost) → Click Create VPC
Key VPC Concepts to Know:
- VPC name (Web-VPC) with CIDR visible
- Subnets list showing public subnets
Launch Template
Go to EC2 → Left sidebar → Launch Templates → Click Create launch template
Enter name: Web-LT
Under Application and OS Images → Search and select Amazon Linux 2023 AMI
Instance type: t2.micro
Under Network settings → Create security group → Name it Web-SG
In Inbound rules → Add two rules: SSH (port 22) and HTTP (port 80), both from 0.0.0.0/0
Scroll down to Advanced details → Paste this in User Data:
#!/bin/bash dnf install -y nginx systemctl enable nginx systemctl start nginx echo "Hello from $(hostname)" > /usr/share/nginx/html/index.html
Click Create launch template
- Launch template name (Web-LT)
- Instance type (t2.micro)
- Security group (Web-SG with SSH + HTTP)
- User Data section
Application Load Balancer
Go to EC2 → Left sidebar → Load Balancers → Create load balancer
Choose Application Load Balancer
Name: Web-ALB
Scheme: Internet-facing (not internal)
Under Listeners → Protocol: HTTP, Port: 80
Select at least 2 Availability Zones (select your VPC and subnets)
For security group → select Web-SG
For now, you can skip target group (we create it next) → click Create load balancer
- Load Balancer name (Web-ALB)
- Scheme (Internet-facing)
- Listener (HTTP port 80)
Target Group
Go to EC2 → Left sidebar → Target Groups → Create target group
Target type: Instances
Name: Web-TG
Protocol: HTTP, Port: 80
Under Health checks → Health check path: /
Click Next → Skip adding instances for now → Click Create target group
- Target group name (Web-TG)
- Health check path (/)
Auto Scaling Group
Go to EC2 → Left sidebar → Auto Scaling Groups → Create Auto Scaling group
Name: Web-ASG
Launch template: Select Web-LT → Click Next
Choose your VPC and select at least 2 subnets → Click Next
Attach to load balancer → Select Attach to an existing load balancer → Choose Web-TG
Set capacity:
Click Next through remaining steps → Create Auto Scaling group
- ASG name (Web-ASG)
- Capacity (Min:2, Desired:2, Max:4)
- Target group attachment (Web-TG)
Scaling Policy
Go to Auto Scaling Groups → Click on Web-ASG
Click the Automatic scaling tab → Create dynamic scaling policy
Policy type: Target tracking scaling
Scaling policy name: Web-CPU-Policy
Metric type: Average CPU utilization
Target value: 50 (scale when CPU goes above 50%)
Click Create
- Policy name (Web-CPU-Policy)
- CPU metric selected
EBS Volume
Go to EC2 → Left sidebar → Volumes → Create volume
Size: 8 GiB (default is fine)
Availability Zone: Must match the AZ of your EC2 instance
Click Create volume → After created, add a Name tag: Web-EBS
Select the volume → Actions → Attach volume → Choose one of your running instances → Click Attach
- Volume name (Web-EBS)
- Attached instance ID visible
EFS (Elastic File System)
Go to EFS service (search in top bar) → Create file system
Name: Web-EFS
VPC: Select your VPC → Click Create
Click on Web-EFS → Go to Network tab → You will see mount targets for each AZ
- File system name (Web-EFS)
- Mount targets shown (with AZ info)
S3 Bucket
Go to S3 service → Create bucket
Bucket name: web-static-storage-sijomon (use your actual name, all lowercase)
Region: Select your region
Leave everything else default → Create bucket
- Bucket name visible (web-static-storage-yourname)
Storage Mount (SSH into EC2)
You need to SSH into your EC2 instance and run these commands.
Step A — Mount EBS to /data
# Check what the EBS device name is lsblk # You will see something like /dev/xvdf or /dev/nvme1n1 # Format the disk (only first time!) sudo mkfs.ext4 /dev/xvdf # Create the mount point sudo mkdir /data # Mount it sudo mount /dev/xvdf /data # Verify df -h
Step B — Mount EFS to /shared
# Install EFS utilities sudo dnf install -y amazon-efs-utils # Create the mount point sudo mkdir /shared # Go to EFS console → Click Web-EFS → Copy the mount command # It will look like this (replace fs-xxxxxxxx with your EFS ID): sudo mount -t efs -o tls fs-xxxxxxxx:/ /shared # Verify both /data and /shared are visible df -h
- /data visible in df -h output
- /shared visible in df -h output
Target Group Health Check
Go to EC2 → Target Groups → Click on Web-TG
Click the Targets tab at the bottom
You should see your 2 instances (from ASG) with status Healthy
Copy the DNS name of Web-ALB → Paste in browser → You should see:Hello from ip-xxx-xxx-xxx-xxx
- Both instances showing "Healthy" status in Web-TG
Create AMI (Machine Image)
An AMI is a snapshot/copy of your EC2 instance. You can use it to launch identical instances quickly — very useful for scaling.
Go to EC2 → Instances → Select one of your running instances
Click Actions → Image and templates → Create image
Image name: Web-AMI
Leave everything else default → Click Create image
Go to AMIs (left sidebar under Images) → Wait for status to change to Available
Why AMI is useful in exam:
# Without AMI → every new instance runs User Data script from scratch # With AMI → every new instance already has nginx + your app ready # Faster boot, consistent environment ✓
- AMI name (Web-AMI) with status "Available"
- Source instance ID visible
CloudWatch — Monitoring
CloudWatch monitors your AWS resources. You can set alarms when CPU, memory, or other metrics go too high.
Part A — View EC2 Metrics
Go to CloudWatch → Left sidebar → Metrics → All metrics
Click EC2 → Per-Instance Metrics → Search CPUUtilization → Select your instance
You will see a graph of CPU usage over time
Part B — Create an Alarm
Go to CloudWatch → Alarms → Create alarm
Click Select metric → EC2 → Per-Instance Metrics → Select CPUUtilization for your instance → Click Select metric
Threshold: Greater than 70 (alarm when CPU > 70%)
Skip notification for now → Alarm name: Web-CPU-Alarm → Click Create alarm
Part C — View ASG Metrics (Dashboard)
Go to CloudWatch → Dashboards → Create dashboard → Name it Web-Dashboard
Add a widget → Line chart → Select EC2 CPUUtilization → Save
- Alarm name (Web-CPU-Alarm) with threshold visible
- Metric graph showing CPUUtilization
Deploy App from GitHub + Database
This is the "simple website with database backend" part. The plan: store your website code on GitHub → clone it on EC2 → connect it to a MySQL database.
Step A — Create a simple app on GitHub (do this before exam)
Create a repo on GitHub with these files:
# File: index.html
<!DOCTYPE html>
<html>
<head><title>My AWS App</title></head>
<body>
<h1>Hello from AWS!</h1>
<p>Deployed via GitHub on EC2</p>
</body>
</html>
Step B — SSH into EC2 and clone the repo
# SSH into your EC2 instance first ssh -i your-key.pem ec2-user@<your-ec2-ip> # Install git (if not already installed) sudo dnf install -y git # Clone your GitHub repo git clone https://github.com/yourusername/your-repo-name.git # Copy files to nginx web root sudo cp -r your-repo-name/* /usr/share/nginx/html/ # Restart nginx sudo systemctl restart nginx
Step C — Install MySQL (Database Backend)
# Install MySQL server sudo dnf install -y mysql-server # Start and enable MySQL sudo systemctl enable --now mysqld # Secure the installation (set root password) sudo mysql_secure_installation # Login to MySQL sudo mysql -u root -p # Inside MySQL — create a database and table CREATE DATABASE webappdb; USE webappdb; CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100), email VARCHAR(100) ); INSERT INTO users (name, email) VALUES ('Sijomon', 'test@email.com'); SELECT * FROM users; EXIT;
Step D — Use User Data to auto-deploy from GitHub (in Launch Template)
Update your Web-LT User Data to this so every new instance auto-clones your repo:
#!/bin/bash dnf install -y nginx git mysql-server systemctl enable nginx mysqld systemctl start nginx mysqld # Clone your GitHub repo directly git clone https://github.com/yourusername/your-repo-name.git /tmp/myapp cp -r /tmp/myapp/* /usr/share/nginx/html/ # Show hostname so we know which instance served the request echo "Served by: $(hostname)" >> /usr/share/nginx/html/index.html
- git clone command running successfully in terminal
- MySQL SELECT * FROM users showing data
- Website visible in browser via EC2 IP or ALB DNS