Load-Balanced Auto-Scaling Web on AWS

Build a multi-AZ ALB and Auto Scaling Group that scales on CloudWatch metrics.

Introduction

30 Second Summary

Every time you visit a popular website during a flash sale or a viral moment, something behind the scenes is spreading that traffic across multiple servers and spinning up new ones when demand spikes. Without this, the site would crash under the load.

In this project, you will build a highly available web architecture on AWS that automatically distributes traffic and scales to meet demand. You will configure an Application Load Balancer and an Auto Scaling Group working together as a production-style system.

What You'll Build

A fully operational load-balanced architecture consisting of an Application Load Balancer fronting an Auto Scaling Group of EC2 web servers, with dynamic scaling policies driven by CloudWatch metrics.

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

  • An internet-facing Application Load Balancer with a configured listener, target group, and health checks distributing HTTP traffic across multiple Availability Zones.
  • A security group architecture following least-privilege principles, where EC2 instances only accept traffic from the ALB.
  • An Auto Scaling Group with a Launch Template and a target tracking scaling policy that automatically launches or terminates instances based on CPU utilization.
  • Secret Mission: Stress-test your infrastructure to trigger a scale-out event and watch Auto Scaling launch new instances that automatically register with your ALB.

Are there any prerequisites?

You will need an AWS account with IAM user access. Familiarity with EC2 instances and VPC basics is helpful but not strictly required.

Launch EC2 Instances and Configure Security Groups

Before your load balancer can distribute traffic, it needs something to distribute traffic to. You need web servers running in multiple Availability Zones so the ALB can route requests across them.

In this step, you will create a security group architecture that follows AWS best practices, then launch two EC2 instances that serve a simple web page showing their instance ID.

In this step, get ready to:
  • Create two security groups following the ALB-to-instance least-privilege pattern.
  • Launch two EC2 instances across different Availability Zones with a web server User Data script.
  • Verify both instances are running and correctly locked down.
Create the security group architecture

Your architecture needs two security groups that work together. The ALB security group allows HTTP traffic from the internet. The EC2 security group allows HTTP traffic only from the ALB security group.

Why two security groups?

This is the recommended pattern for load-balanced architectures on AWS. The ALB faces the internet, but your EC2 instances do not accept direct traffic from outside.

By setting the EC2 security group source to the ALB security group (instead of 0.0.0.0/0), only requests that come through the ALB can reach your instances. This is how Security Groups work across all ELB types.

First, create the ALB security group:

  • In the AWS Management Console, type EC2 in the top search bar and select EC2.
  • In the left navigation pane, click Security Groups (under Network & Security).
  • Click Create security group.
  • For Security group name, enter alb-sg.
  • For Description, enter Allow HTTP from internet to ALB.
  • For VPC, select your default VPC.
  • Under Inbound rules, click Add rule.
  • For Type, select HTTP.
  • For Source, select Anywhere-IPv4 (0.0.0.0/0).
  • Click Create security group.

Now create the EC2 security group:

  • In the left navigation pane, click Security Groups again.
  • Click Create security group.
  • For Security group name, enter ec2-sg.
  • For Description, enter Allow HTTP from ALB only.
  • For VPC, select the same default VPC you used for alb-sg.
  • Under Inbound rules, click Add rule.
  • For Type, select HTTP.
  • For Source, select Custom, then search for and select the alb-sg security group you just created.
  • Click Create security group.

What does this security group architecture do?

The alb-sg group allows inbound HTTP (port 80) from the entire internet. This is where your users' requests arrive.

The ec2-sg group allows inbound HTTP only from alb-sg. Your EC2 instances cannot be reached directly from the internet on port 80. Only requests that pass through the ALB are forwarded to them.

Don't see your security group in the source dropdown?

Make sure both security groups are in the same VPC. You can only reference a security group as a source if it belongs to the same VPC (or a peered VPC).

help me fix my security group source selection

Launch two EC2 instances across different Availability Zones

An Application Load Balancer requires subnets in at least two Availability Zones. You will place one instance in each AZ so you can observe the load balancer distributing traffic between them.

  • In the left navigation pane, click Instances.
  • Click Launch instances.
  • For Name, enter web-server-1.
  • Under Application and OS Images, confirm that Amazon Linux 2023 AMI is selected (it is the default).
  • Under Instance type, select t2.micro (free tier eligible).
  • Under Key pair, select Proceed without a key pair from the dropdown. You will not SSH into these instances in this step.
  • Under Network settings, click Edit.
  • For Subnet, select a subnet in the first Availability Zone of your region (for example, us-east-1a). Note which AZ you choose.
  • For Firewall (security groups), select Select existing security group.
  • Choose ec2-sg from the list.

Now add the User Data script that installs a web server:

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

# Retrieve the instance ID using IMDSv2 (required by Amazon Linux 2023)
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
INSTANCE_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id)

# Create a simple web page displaying the instance ID
echo "<h1>Instance ID: $INSTANCE_ID</h1>" > /var/www/html/index.html

What does this User Data script do?

  • yum update -y updates the system packages on the Amazon Linux 2023 instance.
  • yum install -y httpd installs the Apache web server.
  • systemctl start httpd starts the web server immediately.
  • The TOKEN and INSTANCE_ID lines use IMDSv2 (Instance Metadata Service version 2) to securely retrieve the instance's unique ID. Amazon Linux 2023 requires IMDSv2 by default.
  • The final line writes a simple HTML page to the web root so you can see which instance is responding when you visit the load balancer later.
  • Click Launch instance.

Now launch the second instance in a different Availability Zone:

  • Click Launch instances again from the Instances page.
  • For Name, enter web-server-2.
  • Keep the same AMI (Amazon Linux 2023) and instance type (t2.micro).
  • Under Key pair, select Proceed without a key pair.
  • Under Network settings, click Edit.
  • For Subnet, select a subnet in a different Availability Zone from the first instance (for example, us-east-1b).
  • For Firewall (security groups), select Select existing security group and choose ec2-sg.
  • Expand Advanced details and paste the same User Data script from above into the User data text box.
  • Click Launch instance.

Why different Availability Zones?

Elastic Load Balancing distributes traffic across Availability Zones. An ALB requires subnets in at least two AZs. Placing one instance in each AZ gives you high availability. If one AZ goes down, the other continues serving traffic.

Verify both instances are running
  • In the left navigation pane, click Instances.
  • Wait for both web-server-1 and web-server-2 to show Instance state as Running and Status check as 2/2 checks passed. This may take 1-2 minutes.
  • Click on web-server-1 and confirm its Availability Zone in the details pane.
  • Click on web-server-2 and confirm it is in a different Availability Zone.

Why can't I test the web server directly?

Your ec2-sg security group only allows inbound HTTP from the ALB security group. If you try to paste an instance's public IP into your browser, the connection will time out. This is the correct behavior and proves your security group architecture is working.

In the next step, you will create the ALB and access your instances through it.

Instance stuck in Pending state?

Status checks can take a couple of minutes. If an instance stays in Pending for more than 5 minutes, check that you selected a valid subnet and that your VPC has an internet gateway attached (the default VPC always does).

help me troubleshoot my EC2 instance that won't start

Your security group architecture is locked down and both instances are running. Next up, you will create an Application Load Balancer that sits in front of these instances and distributes traffic between them.

Create an Application Load Balancer with Target Groups and Health Checks

Your EC2 instances are running and serving web pages, but they are locked behind a security group that only allows traffic from the ALB. The next step is to build the Application Load Balancer itself so users can actually reach your servers.

In this step, you will create a target group to organize your instances, set up health checks so the ALB knows which instances are alive, and then create the ALB with an HTTP listener that distributes traffic across both Availability Zones.

In this step, get ready to:
  • Create a target group with custom health check settings.
  • Create an internet-facing Application Load Balancer with an HTTP listener.
  • Verify round-robin traffic distribution across both instances.
Create a target group and register your EC2 instances

A target group tells the ALB where to send incoming traffic. You will create one, configure how the ALB checks whether your instances are healthy, and register both instances as targets.

  • In the EC2 console left navigation pane, under Load Balancing, click Target Groups.
  • Click Create target group.
  • For Choose a target type, select Instances.
  • For Target group name, enter my-target-group.
  • Leave Protocol as HTTP and Port as 80.
  • For VPC, select the same VPC your EC2 instances are running in.

Now configure the health check settings so the ALB quickly detects unhealthy targets.

  • Expand the Health checks section.
  • Set Health check protocol to HTTP.
  • Set Health check path to /.
  • Expand Advanced health check settings.
  • Set Healthy threshold to 2.
  • Set Unhealthy threshold to 2.
  • Set Interval to 10 seconds.
  • Click Next.

Now register both EC2 instances as targets.

  • Select both of your running EC2 instances from the list.
  • Click Include as pending below.
  • Click Create target group.

What is a target group?

A target group is the fundamental routing unit for an ALB. It groups the destinations (your EC2 instances) that the load balancer forwards traffic to.

ALB target types include instance, IP, and Lambda. NLB target types include instance, IP, and ALB. GLB target types include instance and IP.

Don't see your instances in the registration list?

Make sure you selected the correct VPC when creating the target group. Instances only appear if they belong to the same VPC as the target group.

Also confirm both instances are in the running state. Stopped or terminated instances will not appear.

help me figure out why my instances aren't showing up in the target group registration list

Create the Application Load Balancer

With your target group ready, you can now create the ALB that sits in front of your instances and distributes incoming HTTP requests.

  • In the EC2 console left navigation pane, click Load Balancers.
  • Click Create load balancer.
  • Under Application Load Balancer, click Create.

Configure the basic settings for your ALB.

  • For Load balancer name, enter my-alb.
  • For Scheme, select Internet-facing.
  • For IP address type, keep IPv4 selected.

Configure the network mapping so the ALB spans both Availability Zones.

  • For VPC, select the same VPC your instances are in.
  • Under Availability Zones and subnets, select a subnet in each of the two Availability Zones where your instances are running.

Why two Availability Zones?

An ALB requires subnets from at least two Availability Zones. This is what makes the architecture highly available. If one AZ goes down, the ALB continues routing traffic to healthy targets in the other AZ.

Attach the security group you created earlier.

  • In the Security groups section, remove any pre-selected default security group.
  • Select alb-sg (the security group that allows HTTP from anywhere).

Configure the listener to forward traffic to your target group.

  • In the Listeners and routing section, confirm the default listener is set to HTTP on port 80.
  • For Default action, select your target group my-target-group from the dropdown.
  • Scroll to the bottom and click Create load balancer.

What are the different ELB types?

Elastic Load Balancing is the umbrella service with four load balancer types:

  • Application Load Balancer (ALB): Layer 7, HTTP/HTTPS, content-based routing (path, host, headers). Uses a round-robin algorithm.
  • Network Load Balancer (NLB): Layer 4, TCP/UDP/TLS, millions of requests per second, ultra-low latency, static IPs per AZ. Uses a flow hash algorithm.
  • Gateway Load Balancer (GLB): Layer 3+4, for third-party virtual appliances (firewalls, IDS/IPS). Uses GENEVE protocol on port 6081 with a 5-tuple flow hash.
  • Classic Load Balancer: Legacy, being deprecated.

ALB creation failing?

Make sure you selected subnets in at least two different Availability Zones. An ALB cannot be created with subnets from only one AZ.

Also verify that the VPC has an internet gateway attached. Internet-facing load balancers require a VPC with an internet gateway.

help me troubleshoot why my ALB creation is failing

Test load balancing and verify health checks

Your ALB needs a minute or two to provision. Once active, the health checks will begin pinging your instances. You will then verify that traffic is distributed evenly.

  • Wait for the ALB State to change from provisioning to active. This typically takes 2-3 minutes.
  • In the left navigation, click Target Groups.
  • Click on my-target-group to open its details page.
  • Click the Targets tab.
  • Wait until both targets show a Health status of healthy. This may take up to 20 seconds with your 10-second interval and threshold of 2.

Now test the load balancing behavior.

  • Go back to Load Balancers in the left navigation.
  • Select my-alb and copy the DNS name from the details panel.
  • Paste the DNS name into a new browser tab and press Enter.
  • Refresh the page several times. You should see the instance ID change between refreshes, proving traffic is being distributed in a round-robin pattern.

How do ALB health checks work?

The ALB sends HTTP requests to each target at the interval you configured (10 seconds). If a target fails the unhealthy threshold number of consecutive checks (2), it is marked unhealthy and removed from rotation.

Once the target passes the healthy threshold number of consecutive checks (2), it is re-added to the rotation. This automatic detection ensures your users never get routed to a broken instance.

Targets stuck in initial or unhealthy state?

If targets show initial for more than a minute, the health checks have not completed yet. Wait a bit longer.

If targets show unhealthy, check that your EC2 instances have a web server running on port 80 and that ec2-sg allows inbound HTTP from alb-sg. The health check path is /, so the web server must respond at the root path.

help me figure out why my targets are unhealthy in the ALB target group

Your load balancer is live and distributing traffic across both Availability Zones. Next up, you will explore advanced ALB features like sticky sessions, cross-zone load balancing, and connection draining.

Explore ALB Advanced Features

Your ALB is distributing traffic evenly across both instances. In a real production environment, you often need more control over how traffic flows.

In this step, you will explore the advanced features that make ALBs production-ready. These include sticky sessions (keeping a user on the same instance), cross-zone load balancing, connection draining, and SSL/TLS termination.

In this step, get ready to:
  • Enable and test sticky sessions on your target group.
  • Understand cross-zone load balancing and connection draining behavior.
  • Review SSL/TLS termination concepts for the SAA exam.
Enable and test sticky sessions

By default, the ALB distributes each request independently using round-robin. Sticky sessions change this behavior by binding a user's session to a specific target for a set duration.

  • In the EC2 console left navigation pane, under Load Balancing, click Target Groups.
  • Click the name my-target-group to open its details page.
  • Click the Attributes tab, then click Edit.
  • Under Target selection configuration, select Turn on stickiness.
  • For Stickiness type, select Load balancer generated cookie.
  • For Stickiness duration, enter 60 seconds.
  • Click Save changes.

What are sticky sessions?

ALB supports two types of sticky sessions. Duration-based stickiness uses a load-balancer-generated cookie named AWSALB (and AWSALBCORS for cross-origin requests). You set the duration, and the ALB handles everything.

Application-based stickiness lets your app set a custom cookie. The ALB generates a companion cookie and routes based on both. Cookie names must be specified per target group.

Stickiness is always defined at the target group level. For NLB, stickiness uses source IP affinity instead of cookies, since NLB operates at Layer 4.

  • Return to your browser tab with the ALB DNS name.
  • Refresh the page 5-10 times rapidly.

You should see the same instance ID on every refresh. The ALB is now routing all your requests to the same target.

  • Wait 60 seconds, then refresh the page again.

After the stickiness duration expires, the session binding is released. On your next request, the ALB may route you to a different instance.

Still seeing different instances on each refresh?

Make sure you clicked Save changes after enabling stickiness. Also confirm the stickiness type is set to Load balancer generated cookie, not application-based.

Why is my sticky session not working on the ALB?

Understand cross-zone load balancing and connection draining

Two features control how traffic flows between Availability Zones and what happens when instances are removed. Both are critical SAA exam topics.

What is cross-zone load balancing?

With cross-zone load balancing enabled, each load balancer node distributes traffic across ALL registered targets in ALL Availability Zones. Without it, traffic stays within each node's own AZ, which can cause uneven distribution if AZs have different target counts.

The defaults differ by load balancer type. For ALB, cross-zone is ON by default and cannot be changed at the load balancer level (only at the target group level). For NLB, it is OFF by default but can be enabled at the load balancer or target group level. For GLB, it is OFF by default.

  • In the left navigation pane, under Load Balancing, click Load Balancers.
  • Select my-alb and review its Attributes tab.

You will see that cross-zone load balancing is enabled. For your ALB, this means traffic is distributed evenly across both AZs regardless of how many targets each AZ has.

  • Navigate back to Target Groups and click my-target-group.
  • Click the Attributes tab.
  • Look for the Deregistration delay value in the Target deregistration management section.

You should see 300 seconds listed as the default deregistration delay.

What is connection draining (deregistration delay)?

When a target is being removed from the target group, the ALB stops sending NEW requests to it. However, in-flight requests are allowed to complete for up to the deregistration delay period.

During this window, the target enters a "draining" state. After the timeout expires, the target transitions to "unused" and all remaining connections are dropped. This is critical for graceful deployments where you do not want users to see errors mid-request.

The timeout range is 0 to 3600 seconds, with 300 seconds as the default. For the SAA exam, remember that when an ASG is behind an ELB, the cooldown period starts when the terminating instance begins connection draining, not when draining finishes.

Understand SSL/TLS termination

SSL/TLS termination is a theory-only topic for this project because it requires a registered domain and certificate. Understanding where termination happens and how SNI works is heavily tested on the SAA exam.

How does SSL/TLS termination work on ELB?

You attach an SSL/TLS certificate from AWS Certificate Manager (ACM) to an HTTPS listener on your load balancer. SSL termination happens at the load balancer itself. Traffic between the ALB and your backend targets can then travel over plain HTTP, offloading the encryption overhead from your instances.

ALB supports Server Name Indication (SNI), which allows you to host multiple SSL certificates on a single listener. Each certificate maps to a different domain. NLB also supports TLS termination with SNI.

For the SAA exam, focus on three things: where termination happens (at the load balancer), SNI support for multiple domains, and the integration with ACM for automatic certificate management.

Cross-zone defaults summary for the SAA exam

  • ALB: cross-zone ON by default, cannot change at the load balancer level.
  • NLB: cross-zone OFF by default, can enable at load balancer or target group level.
  • GLB: cross-zone OFF by default.

You now understand the advanced features that make ALBs production-grade. Next up, you will create an Auto Scaling Group that automatically manages the lifecycle of your instances and integrates directly with your ALB.

Create an Auto Scaling Group Attached to the ALB

In the previous step, you explored how your ALB handles sticky sessions, cross-zone load balancing, and connection draining. Those features keep traffic flowing smoothly, but there is still a gap in your architecture.

Right now, you manually launched and registered your EC2 instances. If one of them crashes, nobody replaces it. An Auto Scaling Group solves this by owning the instance lifecycle entirely. It launches, monitors, replaces, and scales your instances automatically.

In this step, get ready to:
  • Create a Launch Template that defines how new instances should be configured.
  • Create an Auto Scaling Group attached to your ALB target group.
  • Verify ASG integration and clean up your manual instances.
Create a Launch Template

A Launch Template is a saved set of instance configuration parameters. Instead of manually configuring each EC2 instance, the template stores your AMI, instance type, security group, and User Data script so Auto Scaling can launch identical instances on demand.

  • In the EC2 console left navigation pane, under Instances, click Launch Templates.
  • Click Create launch template.
  • For Launch template name, enter my-launch-template.
  • Under Auto Scaling guidance, select the checkbox to receive guidance for use with Auto Scaling.
  • Under Application and OS Images (Amazon Machine Image), select Amazon Linux 2023 from the Quick Start list.
  • For Instance type, select t2.micro.
  • For Key pair (login), select Don't include in launch template (you do not need SSH access for this project).
  • Under Network settings, for Firewall (security groups), click Select existing security group and choose ec2-sg.
  • Expand Advanced details 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 -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
INSTANCE_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id)
echo "<h1>Instance ID: $INSTANCE_ID</h1>" > /var/www/html/index.html

What does this script do?

This is the same User Data script your manual instances use. It installs the Apache web server (httpd), starts it, and creates a web page that displays the instance's unique ID.

The TOKEN and INSTANCE_ID lines use IMDSv2 (Instance Metadata Service version 2) to securely retrieve the instance ID. This is the default metadata access method on Amazon Linux 2023.

  • Click Create launch template.

You should see a success banner confirming that my-launch-template was created.

Why Launch Templates instead of Launch Configurations?

Launch Templates replaced the older Launch Configurations. Templates are versioned, so you can update settings without recreating the template from scratch. They also support more features like mixed instance types and Spot instance configurations.

Don't see ec2-sg in the security group dropdown?

Make sure you are in the same AWS region where you created your security groups in Step 1. The region selector is in the top-right corner of the console.

help me find my ec2-sg security group in the Launch Template network settings

Create the Auto Scaling Group

Now you will create an Auto Scaling Group that uses your Launch Template to manage instances automatically. The ASG will attach to your existing target group so new instances are automatically registered with the ALB.

  • In the EC2 console left navigation pane, scroll down to Auto Scaling and click Auto Scaling Groups.
  • Click Create Auto Scaling group.
  • For Auto Scaling group name, enter my-asg.
  • For Launch template, select my-launch-template.
  • Click Next.
  • On the Choose instance launch options page, under Network, select the same VPC your ALB uses.
  • For Availability Zones and subnets, select the same two subnets (in both AZs) that your ALB is deployed in.
  • Click Next.
  • On the Configure advanced options page, under Load balancing, select Attach to an existing load balancer.
  • Select Choose from your load balancer target groups.
  • In the Existing load balancer target groups dropdown, select my-target-group.
  • Under Health checks, for Additional health check types, select Turn on Elastic Load Balancing health checks.
  • Click Next.
  • On the Configure group size and scaling page, set Desired capacity to 2, Min desired capacity to 1, and Max desired capacity to 4.
  • Click Skip to review.
  • Review your settings, then click Create Auto Scaling group.

What does attaching to a target group do?

When an Auto Scaling Group is attached to a target group, instances launched by the ASG are automatically registered with that target group. Instances terminated by the ASG are automatically deregistered. You never have to manually add or remove instances from the load balancer again.

Enabling ELB health checks means the ASG will replace instances that the ALB marks unhealthy. Without this setting, the ASG only replaces instances that fail EC2 status checks (hardware or connectivity failures). With ELB health checks, an instance that stops responding to HTTP requests also gets replaced.

Don't see my-target-group in the dropdown?

Ensure you selected Attach to an existing load balancer (not Attach to a new load balancer). Then choose Choose from your load balancer target groups. Your target group must be in the same VPC and region as the ASG.

help me attach my Auto Scaling Group to my existing target group

Verify ASG integration and clean up manual instances

Your ASG will immediately launch 2 new instances to meet the desired capacity. Once they register as healthy, you can safely remove the manual instances you launched in Step 1.

  • In the Auto Scaling Groups page, click on my-asg.
  • Click the Instance management tab.
  • Wait until you see 2 instances with Lifecycle showing InService and Health status showing Healthy. This may take 1-2 minutes.

What should I see?

The Instance management tab should display 2 instances, both in InService lifecycle state. These are brand new instances launched by the ASG using your Launch Template.

Now confirm these new instances are healthy in the target group.

  • In the left navigation pane, under Load Balancing, click Target Groups.
  • Click on my-target-group.
  • Click the Targets tab.
  • You should see 4 targets listed: 2 original manual instances and 2 new ASG-managed instances.
  • Wait until the 2 new instances show healthy status.

Now deregister and terminate the original manual instances.

  • In the Targets tab of my-target-group, select the 2 original instances (the ones you manually launched in Step 1).
  • Click Deregister.
  • In the left navigation pane, click Instances.
  • Select the same 2 original instances.
  • Click Instance state, then Terminate instance.
  • Click Terminate to confirm.

Identify your manual instances

You can tell which instances are manual by checking whether they appear in the ASG's Instance management tab. Instances NOT listed there are your original manual ones. You can also compare instance IDs between the target group and the ASG.

Verify the ALB now routes only to ASG-managed instances.

  • Return to Target Groups and click on my-target-group.
  • On the Targets tab, confirm only 2 targets remain, both showing healthy status.
  • Copy the ALB DNS name from Load Balancers in the left navigation, and open it in your browser.
  • Refresh several times. You should see the instance ID alternating between your 2 new ASG-managed instances.

The ASG now owns the lifecycle

From this point forward, the Auto Scaling Group maintains your desired capacity of 2 instances. If an instance becomes unhealthy (detected by ELB health checks or EC2 status checks), the ASG automatically terminates it and launches a replacement.

The replacement instance is automatically registered with the target group and begins receiving traffic once healthy. In the next step, you will configure scaling policies that let the ASG dynamically adjust capacity based on CloudWatch metrics.

New ASG instances not showing as healthy?

Give the health checks 1-2 minutes to complete. The health check interval is set to 10 seconds with a healthy threshold of 2, so it takes at least 20 seconds for a target to register as healthy.

If targets stay in initial or unhealthy state, verify that ec2-sg allows inbound HTTP on port 80 from alb-sg. Also confirm the User Data script was pasted correctly in your Launch Template.

help me troubleshoot unhealthy targets in my Auto Scaling Group

Your architecture now self-heals. Next up, you will configure dynamic scaling policies that adjust your instance count based on real-time CPU metrics from CloudWatch.

Configure Dynamic Scaling Policies and CloudWatch Alarms

Your Auto Scaling Group is now attached to the ALB and maintaining two healthy instances. If an instance fails, the group replaces it automatically.

But what happens when traffic increases? Right now, the group stays at two instances regardless of load. In this step, you'll create a scaling policy that automatically adjusts capacity based on CPU utilization, and you'll explore the CloudWatch alarms that make it work.

In this step, get ready to:
  • Create a target tracking scaling policy on your Auto Scaling Group.
  • Examine the auto-created CloudWatch alarms and understand scaling metrics.
  • Review scaling cooldown behavior and predictive scaling concepts.
Create a target tracking scaling policy

A target tracking policy works like a thermostat. You set a target metric value, and Auto Scaling adjusts capacity to keep the actual metric near that target.

  • Return to the EC2 console and select Auto Scaling Groups in the left navigation pane.
  • Select the checkbox next to my-asg.
  • In the split pane that opens at the bottom, click the Automatic scaling tab.
  • Under Dynamic scaling policies, click Create dynamic scaling policy.
  • For Policy type, keep the default of Target tracking scaling.
  • Enter a policy name such as cpu50-target-tracking.
  • For Metric type, select Average CPU utilization.
  • For Target value, enter 50.
  • For Instance warmup, enter 60 seconds.
  • Click Create.

What are dynamic scaling policy types?

Amazon EC2 Auto Scaling supports four types of dynamic scaling policies:

  • Target Tracking: You set a target metric value. Auto Scaling automatically creates and manages CloudWatch alarms (AlarmHigh and AlarmLow) to keep the metric near your target. AWS strongly recommends this type.
  • Step Scaling: You create CloudWatch alarms manually and define step adjustments based on the size of the alarm breach.
  • Simple Scaling: A single scaling adjustment with a cooldown period between each scaling activity. This is the default policy type.
  • Predictive Scaling: Uses machine learning to analyze historical CloudWatch data and forecast future demand. Pre-scales capacity ahead of predicted traffic spikes.

The metric you choose must change inversely proportional to capacity. If you double the instance count, the metric should decrease by 50%. CPU utilization and request count per target both satisfy this requirement.

Your policy should now appear under Dynamic scaling policies on the Automatic scaling tab.

Policy not appearing?

Make sure you clicked Create at the bottom of the form. If you see a validation error, confirm that your Max desired capacity is greater than your current Desired capacity (Max should be 4).

help me troubleshoot why my target tracking policy didn't save

Examine the CloudWatch alarms and scaling metrics

When you created the target tracking policy, Auto Scaling automatically created two CloudWatch alarms on your behalf. These alarms trigger the scale-out and scale-in actions.

  • Open the CloudWatch console at https://console.aws.amazon.com/cloudwatch/.
  • In the left navigation pane, click Alarms, then All alarms.
  • Look for two alarms with names starting with TargetTracking-my-asg-AlarmHigh and TargetTracking-my-asg-AlarmLow.
  • Click on the AlarmHigh alarm name to view its details.

You should see both alarms in the list. The AlarmHigh alarm triggers a scale-out action when CPU exceeds your 50% target. The AlarmLow alarm triggers scale-in when CPU drops sufficiently below the target.

What scaling metrics should I know for the SAA exam?

Target tracking supports these predefined metrics:

  • ASGAverageCPUUtilization: Average CPU utilization across all instances in the group.
  • ASGAverageNetworkIn: Average bytes received on all network interfaces.
  • ASGAverageNetworkOut: Average bytes sent out on all network interfaces.
  • ALBRequestCountPerTarget: Average ALB request count per target. Requires a ResourceLabel identifying the target group.

You can also publish custom metrics (like memory utilization or queue depth) and use those for scaling. The key requirement is that the metric must decrease proportionally as capacity increases.

Review scaling cooldown and predictive scaling

Cooldown periods prevent your Auto Scaling Group from launching or terminating instances too rapidly. Understanding how cooldowns interact with different policy types is important for the SAA exam.

  • Return to the EC2 console and select Auto Scaling Groups in the left navigation pane.
  • Select my-asg and click the Details tab in the split pane.
  • Scroll to Advanced configurations and note the Default cooldown value of 300 seconds.

How does scaling cooldown work?

Cooldown applies only to Simple Scaling policies. After a scaling activity completes, the ASG waits the cooldown period (default 300 seconds) before allowing another simple scaling action. You can set policy-specific cooldowns that override the group default.

Target Tracking and Step Scaling use instance warmup instead. During warmup, a newly launched instance is not counted toward the aggregated metrics. This prevents premature scaling decisions while the instance initializes.

When behind a load balancer, the cooldown period starts when the terminating instance begins connection draining (deregistration delay), not when draining finishes.

Predictive Scaling is the fourth policy type. It uses machine learning to analyze historical CloudWatch data and forecast future demand.

What is predictive scaling?

Predictive Scaling pre-scales capacity ahead of predicted traffic spikes. It works best with recurring patterns like daily or weekly cycles. It requires at least 24 hours of historical data to generate forecasts, so it cannot be demonstrated live in this project.

Best practice is to use predictive scaling alongside a dynamic policy (like target tracking) for real-time adjustments. Predictive scaling handles the expected baseline while target tracking responds to unexpected spikes.

Secret mission

Stress-Test Your Auto Scaling Architecture

Your Auto Scaling Group has scaling policies configured, but you have not seen them fire yet. In this secret mission, you will generate real CPU load on one of your instances to trigger the AlarmHigh alarm and watch Auto Scaling launch a new instance in real time. Then you will observe the full scale-in lifecycle after the load subsides.

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. The ALB charges approximately $0.02/hour while active, and EC2 instances may incur charges if your account is past the 12-month free tier period.

Cost warning

Your Application Load Balancer incurs charges for every hour it runs, even with no traffic. If you are not continuing to experiment immediately, delete your resources to avoid unexpected costs.

Resources you used:

  • Auto Scaling Group (my-asg) with EC2 instances.
  • Application Load Balancer (my-alb).
  • Target Group (my-target-group).
  • Launch Template (my-launch-template).
  • Security Groups (alb-sg and ec2-sg).
  • CloudWatch Alarms (auto-created by the target tracking policy).

Keep everything running

No action needed. Choose this if you are still actively experimenting with your load-balanced architecture.

  • Your ALB will continue to charge approximately $0.02/hour.
  • EC2 instances managed by the ASG will continue running (free tier eligible if t2.micro and your account is under 12 months).
  • CloudWatch alarms remain within the free tier (up to 10 alarms).

Pause - I'll come back to this later

Reduce costs by stopping your EC2 instances while keeping your configuration intact for later.

  • In the EC2 console, select your Auto Scaling Group (my-asg).
  • Edit the group size: set Min to 0, Desired to 0, and Max to 4.
  • Click Update. The ASG will terminate all running instances.
  • Note: Your ALB still charges approximately $0.02/hour even with no healthy targets. To fully pause costs, delete the ALB as described in the Delete tab and recreate it later using your saved target group and launch template.

Delete - I don't want to use this again

Remove all project resources and stop all charges. Delete in this specific order to avoid dependency errors.

Step 1: Delete the Auto Scaling Group.

  • In the EC2 console navigation pane, choose Auto Scaling Groups.
  • Select the check box next to my-asg and choose Actions, Delete.
  • Type delete to confirm, then choose Delete.
  • Wait a few minutes for the ASG and its instances to be fully deleted. This also removes the scaling policies and CloudWatch alarms.

Step 2: Delete the Application Load Balancer.

  • In the navigation pane, choose Load Balancers.
  • Select my-alb, then choose Actions, Delete load balancer.
  • Type confirm and choose Delete.

Step 3: Delete the Target Group.

  • In the navigation pane, under Load Balancing, choose Target Groups.
  • Select my-target-group and choose Actions, Delete.
  • Choose Yes, delete to confirm.

Step 4: Delete the Launch Template.

  • In the navigation pane, under Instances, choose Launch Templates.
  • Select my-launch-template and choose Actions, Delete template.
  • Type Delete to confirm, then choose Delete.

Step 5: Delete the Security Groups.

  • In the navigation pane, under Network & Security, choose Security Groups.
  • Select ec2-sg first (because alb-sg is referenced in its inbound rules). Choose Actions, Delete security groups, and confirm.
  • Then select alb-sg and choose Actions, Delete security groups, and confirm.

Wait between deletions

If you get a dependency error when deleting a security group, wait 1-2 minutes for AWS to fully release the references from the deleted ALB or instances. Then try again.

That's a Wrap!

That's a Wrap!

Nice work! You just built a production-style, highly available web architecture on AWS that automatically scales to meet demand.

You've learned how to:

  • Built a multi-AZ Application Load Balancer with target groups, health checks, and security groups following the least-privilege pattern.
  • Created an Auto Scaling Group with a Launch Template that automatically scales capacity and registers instances with the ALB.
  • Configured a target tracking scaling policy driven by CloudWatch alarms and explored all ELB types, sticky sessions, cross-zone load balancing, and connection draining.
  • Secret Mission: Stress-tested the infrastructure and watched Auto Scaling respond in real-time by triggering a scale-out event and observing new instances register with the ALB.

Ready to quiz yourself?