Auto Scaling and Load Balancing on AWS

Build a multi-AZ auto-scaling architecture with an Application Load Balancer.

Introduction

30 Second Summary

Think about a popular website during a flash sale. Traffic spikes unpredictably, and the servers behind that site need to grow and shrink automatically to keep up.

In this project, you will build a production-ready auto-scaling architecture on AWS that launches new servers when demand spikes and removes them when things calm down.

What You'll Build

A multi-AZ, auto-scaling web application architecture using an Application Load Balancer to distribute HTTP traffic and an Auto Scaling Group to manage EC2 instances based on CPU utilization.

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

  • A versioned Launch Template with user data that bootstraps web servers automatically.
  • An internet-facing Application Load Balancer distributing traffic across multiple Availability Zones with health checks confirming instance readiness.
  • An Auto Scaling Group with a Target Tracking scaling policy that maintains CPU utilization at 50%.
  • Secret Mission: Configure path-based routing so the ALB routes different URL paths to different sets of instances.

Are there any prerequisites?

This project assumes you have an AWS account and basic familiarity with EC2 (launching instances, security groups, VPCs).

It is part of a series. Completing Projects 1-3 (EC2 Fundamentals, EBS Storage, Networking and Security) first will give you the foundational knowledge for this build.

Create a Launch Template with User Data

For your Auto Scaling Group to launch identical instances automatically, it needs a blueprint. That blueprint is called a Launch Template.

In this step, you will create a security group to control web traffic, then build a Launch Template that defines exactly how each instance should be configured. The template includes a user data script that turns every new instance into a working web server the moment it boots.

In this step, get ready to:
  • Create a security group that allows HTTP and SSH traffic.
  • Build a Launch Template with a user data script that installs Apache and displays instance metadata.
  • Validate the template by launching a test instance and confirming it serves a web page.
Create a security group for the web tier

A security group acts as a virtual firewall for your instances. You need one that allows web browsers to reach your server on port 80 and lets you connect via SSH for management.

  • Open the EC2 console.
  • In the navigation pane on the left, choose Security Groups.
  • Choose Create security group.
  • For Security group name, enter web-server-sg.
  • For Description, enter Allow HTTP from ALB and SSH for management.
  • For VPC, select your default VPC.

Now add two inbound rules to control what traffic can reach your instances.

  • Under Inbound rules, choose Add rule.
  • For the first rule, set Type to HTTP.
  • Set Source to 0.0.0.0/0 (allows traffic from anywhere).
  • Choose Add rule again to add a second rule.
  • For the second rule, set Type to SSH.
  • Set Source to My IP (this auto-fills your current IP address).
  • Choose Create security group.

You should see the new web-server-sg security group in the list with a Security Group ID (starting with sg-). Note this ID for later.

Why allow HTTP from 0.0.0.0/0?

For now, this opens port 80 to the entire internet so you can test your instances directly. In Step 2, after you create the Application Load Balancer, you could tighten this rule to accept HTTP only from the ALB's security group. This layered approach is a best practice for production architectures.

Don't see Security Groups in the navigation pane?

Make sure you are in the EC2 console, not the VPC console. Security Groups appears under Network & Security in the EC2 navigation pane. If the pane is collapsed, click the hamburger menu icon in the top-left to expand it.

Help me find Security Groups in the EC2 console.

Create the Launch Template

A Launch Template stores every configuration detail your instances need: the AMI, instance type, security group, and startup script. The Auto Scaling Group will use this template to launch identical instances on demand.

  • In the EC2 console navigation pane, under Instances, choose Launch Templates.
  • Choose Create launch template.
  • For Launch template name, enter web-server-template.
  • For Template version description, enter v1 - Apache web server with instance metadata page.
  • Check the Auto Scaling guidance checkbox. This provides best-practice defaults for templates used with Auto Scaling.

Now configure the instance settings.

  • Under Application and OS Images, choose Quick Start.
  • Select Amazon Linux, then choose the Amazon Linux 2023 AMI (marked Free tier eligible).
  • For Instance type, select t3.micro.
  • For Key pair, select an existing key pair or choose Create new key pair to create one.
  • Under Network settings, for Firewall (security groups), choose Select existing security group and pick web-server-sg.

Next, add a user data script that will run automatically every time an instance launches from this template.

  • Expand the Advanced details section at the bottom of the page.
  • Scroll down to the User data field and paste the following script:
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd

TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
INSTANCE_ID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id)
AZ=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/availability-zone)

cat <<EOF > /var/www/html/index.html
<!DOCTYPE html>
<html>
<head><title>Auto Scaling Demo</title></head>
<body>
<h1>Hello from EC2 Auto Scaling!</h1>
<p>Instance ID: $INSTANCE_ID</p>
<p>Availability Zone: $AZ</p>
</body>
</html>
EOF

What does this user data script do?

The script runs once at boot and turns the instance into a working web server:

  • It installs and starts the Apache HTTP server (httpd).
  • It fetches a session token from the Instance Metadata Service v2 (IMDSv2), then uses that token to retrieve the instance ID and Availability Zone.
  • It writes an HTML page that displays the instance ID and AZ. When you load balance across multiple instances later, refreshing the page shows different IDs, proving traffic is being distributed.
  • Choose Create launch template.

You should see a success message confirming that web-server-template was created.

Getting a validation error?

Make sure you selected a key pair. The Auto Scaling guidance checkbox enforces that a key pair is specified. Also confirm you chose web-server-sg under Network settings, not the default security group.

Help me troubleshoot my Launch Template creation error.

✔️ Awesome, I've got everything!

Great. Make sure you noted the template name (web-server-template) because you will reference it when creating the Auto Scaling Group later.

ⓧ I'd like to double check the full code

Here is the complete user data script for your Launch Template:

#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd

TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
INSTANCE_ID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id)
AZ=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/availability-zone)

cat <<EOF > /var/www/html/index.html
<!DOCTYPE html>
<html>
<head><title>Auto Scaling Demo</title></head>
<body>
<h1>Hello from EC2 Auto Scaling!</h1>
<p>Instance ID: $INSTANCE_ID</p>
<p>Availability Zone: $AZ</p>
</body>
</html>
EOF
Validate the template by launching a test instance

Before wiring this template into an Auto Scaling Group, you should confirm it actually produces a working web server. A quick test launch catches any issues with the user data script early.

  • From the Launch Templates page, select web-server-template.
  • Choose Actions, then Launch instance from template.
  • Leave all settings at their defaults and choose Launch instance.
  • Navigate to Instances in the navigation pane and wait for the new instance to show Running in the Instance state column.
  • Select the instance and copy its Public IPv4 address from the Details tab.
  • Paste the IP address into a new browser tab. You should see a page titled Auto Scaling Demo with the instance ID and Availability Zone displayed.

Give it a minute

If the page does not load immediately, wait 1-2 minutes. The user data script needs time to install Apache and create the HTML file after the instance boots.

Once you have confirmed the page loads, terminate the test instance so it does not incur charges.

  • Select the test instance in the Instances list.
  • Choose Instance state, then Terminate instance.
  • Confirm the termination when prompted.

What is the difference between a Launch Template and a Launch Configuration?

Launch Templates support versioning, multiple instance types, and a mix of Spot and On-Demand instances. Launch Configurations are legacy. They cannot be edited (you must create a new one for any change) and do not support newer EC2 features.

AWS recommends Launch Templates for all new Auto Scaling groups. On the SAA-C03 exam, if a question mentions "Launch Configuration," it is usually testing whether you know this is the older approach.

Page not loading after 2 minutes?

Check that your security group web-server-sg has an inbound rule allowing HTTP on port 80 from 0.0.0.0/0. Also confirm the instance has a public IP address (check the Public IPv4 address field in the Details tab). If it shows a dash, the subnet may not auto-assign public IPs.

My test instance is not serving the web page. Help me debug.

Your Launch Template is validated and ready. Next up, you will create an Application Load Balancer to distribute traffic across multiple instances that the Auto Scaling Group launches from this template.

Create an Application Load Balancer and Target Group

Your Launch Template is locked and loaded. It proved it can launch a working web server with a single click.

Now you need a way to distribute incoming traffic across multiple instances. An Application Load Balancer accepts HTTP requests from users and forwards them to healthy instances registered in a Target Group. In this step, you will create both.

In this step, get ready to:
  • Create a Target Group with custom health check settings.
  • Deploy an internet-facing Application Load Balancer across multiple Availability Zones.
  • Understand the difference between EC2 and ELB health checks for the SAA-C03 exam.
Create a Target Group

A Target Group defines which instances receive traffic and how the load balancer checks if they are healthy. You will configure health check settings so unhealthy instances are detected quickly.

  • In the EC2 Console navigation pane, under Load Balancing, choose Target Groups.
  • Click Create target group.
  • For Choose a target type, select Instances.
  • Enter web-server-tg in the Target group name field.
  • Set Protocol to HTTP and Port to 80.
  • For VPC, select your default VPC.
  • Under Health checks, confirm Health check protocol is HTTP and Health check path is /.
  • Expand Advanced health check settings.
  • Set Healthy threshold to 2.
  • Set Unhealthy threshold to 3.
  • Set Timeout to 5 seconds.
  • Set Interval to 10 seconds.
  • Set Success codes to 200.
  • Click Next.
  • Do NOT register any targets on the next page. The Auto Scaling Group will handle registration automatically in the next step.
  • Click Create target group.

Why custom health check values?

The default health check interval is 30 seconds with a healthy threshold of 5. That means it takes 150 seconds (5 successful checks at 30s intervals) before a new instance is marked healthy.

By lowering the interval to 10 seconds and the healthy threshold to 2, you reduce this to just 20 seconds. For a demo environment, this lets you observe scaling behavior much faster.

Don't see your default VPC in the dropdown?

If you deleted your default VPC in a previous project, you can recreate it. In the VPC Console, choose Actions, then Create default VPC. Return to the Target Group creation page and refresh.

Help me find or recreate my default VPC

Create the Application Load Balancer

Before creating the ALB itself, you need a dedicated security group that allows inbound HTTP traffic from the internet. This keeps the ALB's firewall rules separate from your web server instances.

  • In the EC2 Console navigation pane, choose Security Groups.
  • Click Create security group.
  • Enter alb-sg as the Security group name.
  • Enter Allow HTTP from internet to ALB as the Description.
  • For VPC, select your default VPC.
  • Under Inbound rules, click Add rule.
  • Set Type to HTTP, which auto-fills port 80.
  • Set Source to Anywhere-IPv4 (0.0.0.0/0).
  • Click Create security group.

Why a separate security group for the ALB?

In production, you would update web-server-sg to only accept HTTP traffic from the ALB's security group rather than from the entire internet. This ensures instances are only reachable through the load balancer. For this project, both security groups allow 0.0.0.0/0 to keep things simple.

Now create the Application Load Balancer itself.

  • In the EC2 Console navigation pane, under Load Balancing, choose Load Balancers.
  • Click Create load balancer.
  • Under Application Load Balancer, click Create.
  • Enter web-alb in the Load balancer name field.
  • For Scheme, select Internet-facing.
  • For IP address type, select IPv4.
  • Under Network mapping, select your default VPC.
  • Select subnets in at least 2 Availability Zones (for example, us-east-1a and us-east-1b). Choose whichever AZs are available in your region.
  • Under Security groups, remove the default security group if it is pre-selected.
  • Select the alb-sg security group you just created.
  • Under Listeners and routing, confirm the default listener is HTTP on port 80.
  • For Default action, select Forward to and choose web-server-tg from the dropdown.
  • Click Create load balancer.
  • After creation, select web-alb from the Load Balancers list.
  • On the Description tab, find the DNS name field and copy this value. You will use it later to access your application.

What is the DNS name?

The DNS name is the public URL for your load balancer. It looks something like web-alb-123456789.us-east-1.elb.amazonaws.com. Anyone on the internet can use this URL to reach your ALB, which then forwards the request to a healthy target.

Seeing a validation error during creation?

The most common cause is selecting subnets from only one Availability Zone. The ALB requires subnets from at least two AZs. Go back to Network mapping and check a subnet in a second AZ.

If you see a security group error, confirm you selected alb-sg and that it belongs to the same VPC you chose for the ALB.

Help me troubleshoot my ALB creation error

Understand health check types for the SAA-C03 exam

Health checks determine whether an instance is ready to receive traffic. AWS offers two types that behave very differently when paired with an Auto Scaling Group.

What are EC2 health checks?

EC2 health checks are the default for any Auto Scaling Group. They only verify the instance status at the hypervisor level. If the instance is running (not stopped or terminated), it passes.

This means a crashed web server still appears "healthy" because the underlying instance is fine. The Auto Scaling Group will not replace it.

What are ELB health checks?

When you attach an Auto Scaling Group to a load balancer, you can enable ELB health checks. The ALB sends HTTP requests to the health check path (/ in your case) on each target.

If a target fails these checks, two things happen. The ALB stops routing traffic to it. The Auto Scaling Group terminates the instance and launches a replacement.

SAA-C03 exam trap

If only EC2 health checks are enabled, an instance whose web server has crashed is still considered healthy because the instance itself is running. Always enable ELB health checks when using a load balancer with an Auto Scaling Group. You will do exactly this in the next step.

Your load balancer is live and waiting for targets. In the next step, you will create an Auto Scaling Group that automatically registers instances with this Target Group and scales based on CPU utilization.

Create an Auto Scaling Group with Target Tracking

You have a Load Balancer ready to distribute traffic and a Target Group waiting for instances. The missing piece is the mechanism that actually launches and manages those instances.

An Auto Scaling Group (ASG) ties everything together. It uses your Launch Template to launch instances, registers them with the Target Group, monitors their health through the ALB, and adjusts capacity based on a scaling policy you define. In this step, you will create an ASG with a Target Tracking scaling policy that keeps CPU utilization at 50%.

In this step, get ready to:
  • Create an Auto Scaling Group attached to your ALB Target Group with ELB health checks enabled.
  • Configure a Target Tracking scaling policy targeting 50% average CPU utilization.
  • Verify instances launch, pass health checks, and serve traffic through the ALB.
Create the Auto Scaling Group

The ASG creation wizard walks through several pages. You will configure the Launch Template, network, load balancer attachment, and health checks before reaching the scaling policy page.

  • In the EC2 Console navigation pane, choose Auto Scaling Groups.
  • Choose Create Auto Scaling group.
  • On the Choose launch template or configuration page, for Auto Scaling group name, enter web-asg.
  • For Launch template, select web-server-template.
  • For Launch template version, choose Latest.
  • Choose Next.
  • On the Choose instance launch options page, under Network, for VPC, select the default VPC.
  • For Availability Zones and subnets, select the same 2 or more subnets you chose when creating the ALB.
  • Choose Next.

Match your ALB subnets

The ASG must span the same Availability Zones as your ALB. If you selected us-east-1a and us-east-1b for the ALB, choose those same subnets here.

  • On the Configure advanced options page, in the Load balancing section, choose Attach to an existing load balancer.
  • Select Choose from your load balancer target groups.
  • From the dropdown, select web-server-tg.
  • Under Health checks, for Additional health check types, select Turn on Elastic Load Balancing health checks.
  • For Health check grace period, enter 120 seconds.
  • Choose Next.

Why enable ELB health checks?

By default, the ASG only uses EC2 health checks, which verify the instance is running. They do not confirm your web server is actually responding to HTTP requests.

With ELB health checks enabled, the ALB sends HTTP requests to each instance's health check path. If an instance fails these checks, the ASG terminates and replaces it automatically. This catches crashed applications that EC2 status checks would miss.

The 120-second grace period gives instances time to boot, run the user data script, and start serving before health evaluation begins.

Configure group size and target tracking policy

This page defines how many instances the ASG maintains and how it scales automatically based on CPU load.

  • Under Group size, for Desired capacity, enter 2.
  • Under Scaling limits, set Min desired capacity to 1.
  • Set Max desired capacity to 4.
  • Under Automatic scaling, choose Target tracking scaling policy.
  • For the policy name, enter cpu-50-target-tracking.
  • For Metric type, select Average CPU utilization.
  • For Target value, enter 50.
  • For Instance warmup, enter 60 seconds.
  • Choose Next through the remaining screens (notifications, tags) without making changes.
  • On the Review page, choose Create Auto Scaling group.

What does Target Tracking do?

Target Tracking works like a thermostat. You set a target (50% CPU), and the ASG automatically adds instances when utilization rises above it and removes instances when it drops below.

Behind the scenes, AWS creates two CloudWatch alarms on your behalf. One triggers scale-out when CPU exceeds the target. The other triggers scale-in when CPU drops below it. You do not need to create these alarms manually.

The 60-second instance warmup means newly launched instances are excluded from the CPU metric calculation for 60 seconds. This prevents the ASG from launching too many instances before new ones absorb load.

What are min, desired, and max capacity?

These three values define the scaling boundaries. Min (1) is the floor. The ASG never terminates below this count, even during scale-in. Desired (2) is the current target the ASG maintains right now. Max (4) is the ceiling. The ASG never launches more than this, even during traffic spikes.

The scaling policy adjusts the desired capacity between min and max based on the CPU metric. On the SAA-C03, remember that the ASG operates between min and max. It does not exceed max even if the metric stays above the target.

Verify the ASG launches instances and registers with the ALB

With the ASG created, it immediately begins launching 2 instances (the desired capacity). You need to confirm they pass health checks and the ALB distributes traffic across them.

  • In Auto Scaling Groups, select web-asg.
  • Choose the Activity tab.
  • Verify that 2 instances show as launching in the activity history. The status moves from PreInService to Successful.
  • In the navigation pane, under Load Balancing, choose Target Groups.
  • Select web-server-tg, then choose the Targets tab.
  • Wait until both targets show Health status: healthy. This may take 1-2 minutes.
  • Navigate to Load Balancers in the navigation pane, select web-alb, and copy the DNS name from the Description tab.
  • Open the ALB DNS name in your browser.
  • Refresh the page multiple times. You should see different Instance IDs and Availability Zone names in the response, confirming traffic is balanced across both instances.

Targets stuck in unhealthy status?

If targets remain unhealthy after 2-3 minutes, check that the web-server-sg security group allows inbound HTTP (port 80). The ALB sends health check requests on this port.

Also confirm the health check path is / and the instance's user data script completed successfully. You can verify by connecting to the instance and checking if httpd is running.

help me troubleshoot why my Auto Scaling Group targets are showing unhealthy in the Target Group

What is Cross-Zone Load Balancing?

Cross-Zone Load Balancing is enabled by default on Application Load Balancers. This means each ALB node distributes traffic evenly across all registered targets in all enabled Availability Zones, regardless of which node received the request.

Without cross-zone balancing, each AZ node only routes to targets in its own AZ. This can cause uneven distribution if AZs have different numbers of instances. For the SAA-C03, remember that ALBs have cross-zone enabled by default at no extra charge, while NLBs have it disabled by default and charge for inter-AZ data transfer.

Your Auto Scaling Group is live. Two instances are serving traffic through the ALB, and the Target Tracking policy is monitoring CPU utilization. Next up, you will stress-test the setup to trigger automatic scale-out and watch the ASG launch new instances in real time.

Trigger Scaling and Observe Auto Scaling Behavior

Your Auto Scaling Group is running two healthy instances behind the ALB. The Target Tracking policy is watching average CPU utilization across those instances, ready to add capacity if the metric exceeds 50%.

In this step, you will deliberately stress one instance's CPU to trigger a scale-out event. You will then observe the full scaling lifecycle: new instances launching, registering with the Target Group, receiving traffic from the ALB, and eventually scaling back in when the load drops.

In this step, get ready to:
  • Connect to a running instance and install a CPU stress tool.
  • Trigger a scale-out event by spiking CPU above 50%.
  • Observe scale-in behavior after the load subsides.
Stress-test to trigger scale-out

To see Auto Scaling in action, you need to push CPU utilization above the 50% target on at least one instance. AWS provides a browser-based terminal called EC2 Instance Connect that lets you connect to a running instance without managing SSH keys locally.

  • In the EC2 console navigation pane, choose Instances.
  • Select one of the two running instances launched by web-asg.
  • Choose Connect at the top of the page.
  • Choose the EC2 Instance Connect tab.
  • Choose Connect. An in-browser terminal window opens.

What is EC2 Instance Connect?

EC2 Instance Connect pushes a temporary SSH public key to the instance metadata for 60 seconds, authenticated through your IAM credentials. This means you can open a terminal session directly in the browser without downloading a private key file.

  • Install the stress tool by running this command in the terminal:
sudo dnf install stress -y

What does this command do?

Amazon Linux 2023 uses dnf as its package manager. The stress package generates artificial CPU load, which is exactly what you need to push utilization past the 50% target.

  • Spike the CPU by running this command:
stress --cpu 4 --timeout 300

What does this command do?

This spawns 4 CPU-intensive worker processes that run for 300 seconds (5 minutes). On a t3.micro instance with 2 vCPUs, 4 workers guarantee 100% CPU utilization, well above the 50% target.

You should see the terminal become unresponsive to new input while stress is running. This is normal.

Connection timed out or refused?

Make sure the instance's security group (web-server-sg) has an inbound rule for SSH (port 22) from your IP. EC2 Instance Connect in the browser requires this rule to be in place.

If the instance was recently launched by Auto Scaling, wait 1-2 minutes for the user data script to finish before connecting.

help me connect to my EC2 instance using EC2 Instance Connect

Observe the scaling activity

CloudWatch aggregates CPU metrics over 3 consecutive 1-minute datapoints before target tracking triggers a scaling action. This means you need to wait 3-5 minutes for the scale-out to begin.

  • In the EC2 console navigation pane, choose Auto Scaling Groups.
  • Select web-asg.
  • Choose the Activity tab.
  • Wait 3-5 minutes, then refresh the page. You should see a new activity entry with Status Successful indicating a new instance is launching.
  • Check the Group details section. The Desired capacity should have increased (for example, from 2 to 3).

What is happening behind the scenes?

Target tracking automatically created two CloudWatch alarms when you set up the policy. The AlarmHigh alarm fires when average CPU exceeds 50%, and the ASG responds by increasing desired capacity. The AlarmLow alarm fires when CPU drops below the target, triggering scale-in.

You did not need to create these alarms manually. Target tracking manages them on your behalf.

  • In the navigation pane under Load Balancing, choose Target Groups.
  • Select web-server-tg and choose the Targets tab.
  • Observe the new target appearing. Its status starts as initial (still passing health checks), then changes to healthy.
  • Open the ALB DNS name in your browser and refresh multiple times. You should eventually see a third instance ID appear in the page, confirming the ALB is routing traffic to the new instance.

Instance warmup and metric aggregation

The instance warmup you set to 60 seconds tells the ASG not to count a newly launched instance's metrics toward the aggregate until 60 seconds have elapsed. This prevents the ASG from launching too many instances before new ones can absorb load.

The default cooldown period of 300 seconds applies to simple scaling policies only. Target tracking manages its own cooldown behavior automatically, which is why it is the recommended starting point for most workloads.

SAA-C03 exam note: cross-zone load balancing

Cross-zone load balancing is enabled by default on ALBs at no extra charge. This means each ALB node distributes traffic evenly across all registered targets in all enabled Availability Zones, regardless of which node received the request.

On NLBs, cross-zone load balancing is disabled by default and incurs inter-AZ data transfer charges when enabled. This is a common exam differentiator.

Stop the stress test and observe scale-in

Now that you have confirmed scale-out works, you need to let CPU drop below the target so the ASG scales back in. Scale-in is more gradual than scale-out because target tracking prioritizes availability over cost savings.

  • Switch back to the EC2 Instance Connect terminal window.
  • Press Ctrl+C to stop the stress test. If the 300-second timeout has already passed, the stress processes have stopped automatically.

After CPU drops below the target, the ASG will eventually reduce desired capacity. This can take 5-10 minutes because target tracking scales in conservatively to prevent flapping.

  • Return to the Auto Scaling Groups page in the EC2 console.
  • Select web-asg and choose the Activity tab.
  • Refresh periodically until you see a TerminatingInstances activity entry. This confirms the ASG is removing an instance.
  • Navigate to Target Groups, select web-server-tg, and check the Targets tab. The terminated instance should disappear from the list as it is deregistered.
  • Refresh the ALB DNS name in your browser. You should now see only 2 instance IDs appearing across refreshes.

SAA-C03 exam note: scale-out vs scale-in behavior

Target tracking always tries to keep the metric at the target value. It scales out aggressively to recover from spikes quickly. It scales in conservatively to prevent flapping (repeatedly launching and terminating instances).

This asymmetry is intentional: the cost of a brief over-provision (a few extra cents) is much lower than the cost of dropping user requests during a spike.

Scale-in not happening after 10 minutes?

Verify that the stress process has actually stopped by checking the Instance Connect terminal. If the connection dropped, the stress process may still be running on the instance.

Reconnect to the instance and run top to check CPU usage. If stress is still running, terminate it manually with killall stress.

help me understand why my Auto Scaling Group is not scaling in

You have just validated the complete Auto Scaling lifecycle. Your architecture can automatically add capacity under load and release it when demand drops. Next up, tackle the Secret Mission to add path-based routing to your ALB.

Secret mission

Add Path-Based Routing

Your ALB currently sends all traffic to the same target group. In production, microservices architectures route different URL paths to different backend services. Can you configure the ALB to send /api/* requests to a separate set of instances?

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project uses paid AWS resources that accumulate charges by the hour.

Cost warning

Your Application Load Balancer costs approximately $0.0225 per hour. Each t3.micro instance in your Auto Scaling Groups costs approximately $0.0104 per hour. With 3 instances and 1 ALB running, you are paying roughly $0.05 per hour.

If you are not actively using these resources, delete them to avoid unexpected charges on your AWS bill.

Resources you used:

  • Application Load Balancer (web-alb).
  • Auto Scaling Group (web-asg) with EC2 instances.
  • Auto Scaling Group (api-asg) with EC2 instances.
  • Target Group (web-server-tg).
  • Target Group (api-server-tg).
  • Launch Template (web-server-template).
  • Security Group (web-server-sg).
  • Security Group (alb-sg).

Keep everything running

No action needed. Choose this if you are still actively building or experimenting with your Auto Scaling setup.

  • Your ALB and EC2 instances will continue accumulating hourly charges.
  • Monitor your AWS billing dashboard to track costs.

Pause - I'll come back to this later

Reduce costs by scaling down your Auto Scaling Groups to zero instances while keeping the configuration intact.

  • In the EC2 console, select Auto Scaling Groups from the navigation pane.
  • Select web-asg, choose Edit, and set Desired capacity, Min desired capacity, and Max desired capacity all to 0. Save changes.
  • Repeat for api-asg. Set all capacity values to 0 and save.
  • This terminates all running instances while preserving your ASG configuration, launch template, ALB, and target groups.
  • The ALB still incurs its hourly charge even with no registered targets. Delete the ALB as well if you want to eliminate all costs.
  • To resume later, edit the ASGs and set Desired and Min back to their original values.

Delete - I don't want to use this again

Remove all project resources completely. Follow this order to avoid dependency errors.

Step 1: Delete both Auto Scaling Groups.

  • In the EC2 console, select Auto Scaling Groups from the navigation pane.
  • Select web-asg.
  • Choose Actions, then Delete.
  • Type delete to confirm, then choose Delete.
  • Repeat for api-asg. This automatically terminates all instances in both groups.
  • Wait for both ASGs to disappear from the list before proceeding.

Step 2: Delete the Load Balancer.

  • In the EC2 console, select Load Balancers under Load Balancing in the navigation pane.
  • Select web-alb.
  • Choose Actions, then Delete load balancer.
  • Type confirm and choose Delete.

Step 3: Delete both Target Groups.

  • In the EC2 console, select Target Groups under Load Balancing in the navigation pane.
  • Select web-server-tg.
  • Choose Actions, then Delete. Confirm the deletion.
  • Repeat for api-server-tg.

Step 4: Delete the Launch Template.

  • In the EC2 console, select Launch Templates under Instances in the navigation pane.
  • Select web-server-template.
  • Choose Actions, then Delete template.
  • Type Delete to confirm, then choose Delete. This removes all versions of the template.

Step 5: Delete the Security Groups.

  • In the EC2 console, select Security Groups in the navigation pane.
  • Select web-server-sg.
  • Choose Actions, then Delete security groups. Confirm by typing delete and choosing Delete.
  • Repeat for alb-sg.
  • If you see a dependency error, wait a few minutes for the load balancer network interfaces to be fully removed, then try again.

Verify everything is deleted

Check the EC2 console Instances page. You should see no running instances tagged with your Auto Scaling Groups. Also verify that the Load Balancers, Target Groups, and Launch Templates pages are empty of your project resources.

Nice Work!

Nice Work!

You just built a production-ready, multi-AZ auto-scaling architecture on AWS. Your infrastructure automatically launches and terminates EC2 instances based on real-time CPU demand, with an Application Load Balancer distributing traffic across all healthy targets.

Here's what you learned:

  • Created a versioned Launch Template with user data that bootstraps web servers automatically, and deployed an internet-facing Application Load Balancer with health-check-enabled Target Groups.
  • Built a multi-AZ Auto Scaling Group with a Target Tracking scaling policy that maintains average CPU utilization at 50%, and triggered real scale-out and scale-in events to observe the full scaling lifecycle.
  • Understood key SAA-C03 exam concepts including EC2 vs ELB health checks, min/desired/max capacity, instance warmup vs cooldown, and cross-zone load balancing.
  • Secret Mission: Configured path-based routing with multiple Target Groups, demonstrating the ALB's Layer 7 request-routing capability.

Ready to quiz yourself?