A single EC2 in a single AZ is a single point of failure. The third post in the series puts the compute from Post 02 into the network from Post 01 and turns it into a system that survives an AZ outage and a traffic spike without human intervention. This is the textbook "3-tier" shape — load balancer in front, Auto Scaling Group across AZs, optional managed data behind. The patterns here form the spine of nearly every classical AWS workload.

Series map: 00 Foundations → 01 Network First → 02 Compute & Storage → 03 Scale & Survive (this post) → 04 Managed Data → 05 Serverless & Events → 06 Workflow Orchestration → 07 Observability & Security → 08 IaC.


Learning Outcome

  • Pick between ALB, NLB, GLB, and the legacy CLB.
  • Configure target groups with sane health checks.
  • Build an Auto Scaling Group across multiple AZs.
  • Write scaling policies: target tracking, step, scheduled.
  • Apply the "warm pool" + "instance refresh" pattern for safe rollouts.
  • Combine ALB + ASG + private subnets into the textbook 3-tier reference architecture.

Chapter 1 — The load balancer family

LB Layer Protocols When
Application LB (ALB) 7 HTTP, HTTPS, WebSocket, gRPC web APIs, microservices, host/path routing
Network LB (NLB) 4 TCP, UDP, TLS extreme throughput, static IP, non-HTTP
Gateway LB (GLB) 3 IP via GENEVE inserting 3rd-party network appliances
Classic LB (CLB) 4 + 7 (legacy) HTTP, TCP only existing deployments

Most workloads use ALB. Reach for NLB when you need raw TCP, sub-millisecond latency, or a static IP that doesn't change. GLB is niche (firewall/IDS vendors). CLB is deprecated for new use.

ALB headline features

  • Host-based routing — api.example.com → service A, app.example.com → service B.
  • Path-based routing — /v1/* → service A, /v2/* → service B.
  • Header / query-param matching in listener rules.
  • WebSocket and gRPC support.
  • TLS termination with certs from ACM.
  • AWS WAF integration.
  • Cognito / OIDC authentication at the listener.
  • Sticky sessions via cookies.

NLB headline features

  • Static per-AZ IP (or your own Elastic IPs).
  • Millions of requests/sec with sub-millisecond added latency.
  • Preserves the client source IP at the backend.
  • Supports the PROXY protocol for backends that want the original peer IP.

Chapter 2 — Target groups

A target group is a registered list of "things that can serve traffic". A listener forwards based on rules to one or more target groups.

Target types:

  • instance — by EC2 instance ID
  • ip — for ECS tasks, on-prem hosts via Direct Connect
  • lambda — invoke a Lambda per request (ALB only)
  • alb — chain an ALB behind an NLB (advanced)

Health checks (the make-or-break setting)

Setting Reasonable default
Protocol HTTP / HTTPS
Path /health (your app implements)
Interval 15-30 sec
Timeout 5 sec
Healthy threshold 2
Unhealthy threshold 2-3

Watch out: an overly aggressive check (1 second, 1 failure → unhealthy) marks healthy targets bad during minor blips. An overly relaxed check (60 seconds, 5 failures) lets bad targets serve traffic for minutes.

Three common health-check mistakes

  1. The /health endpoint also checks a downstream DB → a single DB hiccup marks every target unhealthy → outage. Make /health a shallow liveness check; add /ready for dependency health.
  2. The endpoint requires auth. The LB has no credentials → 401 → all targets appear unhealthy.
  3. The endpoint runs an expensive query. ALB poll × target count multiplies the load.

Chapter 3 — Listeners and routing rules

1
2
3
4
5
6
7
ALB
└── Listener: HTTPS 443 (cert from ACM)
├── Rule 1: Host = api.example.com → forward to tg-api
├── Rule 2: Path = /admin/* → forward to tg-admin
├── Rule 3: Header X-Canary = true → forward to tg-canary
└── Default → forward to tg-web

Common patterns:

  • HTTP → HTTPS redirect — a port 80 listener with a single redirect rule to 443.
  • Canary — header/cookie-based routing to a small target group running a new version.
  • Maintenance page — fixed-response rule returning a static 503 + HTML.
  • Weighted target groups — split a percentage of traffic for blue/green.

Chapter 4 — Auto Scaling Group (ASG)

An ASG maintains a fleet of EC2 instances at a target capacity, replaces unhealthy ones automatically, and scales up/down based on policies.

Anatomy

  • Launch template — AMI, instance type, key, SGs, user data. (Replaces older "launch configuration".)
  • Min / Desired / Max — capacity bounds.
  • VPC zone identifier — list of subnets (one per AZ).
  • Health-check type — EC2 (instance status) or ELB (target-group health). Always pick ELB if the ASG is behind a load balancer.
  • Target groups — the ASG registers / deregisters instances automatically as they come and go.

A minimal ASG

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# launch template
aws ec2 create-launch-template \
--launch-template-name web-template \
--launch-template-data '{
"ImageId":"ami-XXXXXXXX",
"InstanceType":"t3.micro",
"SecurityGroupIds":["sg-XXXXXXXX"],
"UserData":"'$(base64 -w0 user-data.sh)'",
"IamInstanceProfile":{"Name":"web-instance-profile"}
}'


# the ASG itself
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name web-asg \
--launch-template LaunchTemplateName=web-template,Version='$Latest' \
--min-size 2 --desired-capacity 2 --max-size 10 \
--vpc-zone-identifier "subnet-aaa,subnet-bbb,subnet-ccc" \
--target-group-arns arn:aws:elasticloadbalancing:... \
--health-check-type ELB --health-check-grace-period 120

user-data.sh runs on first boot — install your app, fetch config, register with discovery.


Chapter 5 — Scaling policies

Target tracking (start here)

Pick a metric and a target; AWS adds/removes instances to keep the metric there.

  • "Average CPU = 50%" → scale out when > 50, in when < 50.
  • "ALB requests per target = 200" → scale to meet load.
1
2
3
4
5
6
7
8
9
aws autoscaling put-scaling-policy \
--auto-scaling-group-name web-asg \
--policy-name cpu-target \
--policy-type TargetTrackingScaling \
--target-tracking-configuration '{
"PredefinedMetricSpecification": { "PredefinedMetricType": "ASGAverageCPUUtilization" },
"TargetValue": 50.0
}'

Step scaling

Trigger at thresholds; add/remove a configurable count per step. Use when target tracking can't express the logic — e.g., asymmetric scale-out vs scale-in speeds.

Scheduled

Predictable patterns deserve scheduled scaling — flash sale, market open, weekday morning ramp.

1
2
3
4
5
6
aws autoscaling put-scheduled-update-group-action \
--auto-scaling-group-name web-asg \
--scheduled-action-name morning-warmup \
--recurrence "0 9 * * 1-5" \
--desired-capacity 20 --min-size 20

Trick: schedule warm-up 15 minutes before a known spike. EC2 + ASG instances take 1-3 minutes from request to "in service".


Chapter 6 — Cooldowns, warm pools, instance refresh

Cooldown

A waiting period after a scaling action before evaluating again — prevents oscillation when metrics dip slightly post-action.

Warm pool

A pool of pre-initialised but stopped instances. When the ASG needs to scale, it starts (rather than provisions) one — seconds vs minutes. Cost: EBS storage of the warm instances.

Instance refresh

When you publish a new launch-template version (new AMI, new user-data), start-instance-refresh rolls existing instances out in batches.

1
2
3
4
aws autoscaling start-instance-refresh \
--auto-scaling-group-name web-asg \
--preferences '{"MinHealthyPercentage":90,"InstanceWarmup":120}'

Pair with ALB weighted target groups or canary listener rules for safe rollouts.


Chapter 7 — The textbook 3-tier reference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
              ┌─────────────────────────────┐
│ Route 53 │ DNS
└────────────┬────────────────┘
│
┌────────────┴────────────────┐
│ ALB (public subnets) │ HTTPS, WAF attached
└────────────┬────────────────┘
│ http → 8080
┌────────────┴────────────────┐
│ ASG of EC2 (private subnets)│ app tier, multi-AZ
└────────────┬────────────────┘
│ tcp → 5432
┌────────────┴────────────────┐
│ RDS multi-AZ (isolated) │ data tier
└─────────────────────────────┘

Tier rules:

  • ALB lives in public subnets across ≥ 2 AZs.
  • App tier lives in private subnets across the same AZs.
  • DB tier lives in isolated subnets, accessible only from the app SG.
  • Each tier's security group only accepts traffic from the tier above's SG.
  • Outbound from app tier to internet via NAT (only for pulling packages, calling external APIs).

This shape survives:

  • a single instance failure (ASG replaces it)
  • a single AZ failure (other AZs absorb traffic)
  • a deploy with bad code (canary catches it; instance refresh rolls back)
  • a traffic spike (ASG scales out)

The three-tier shape: ALB in public subnets, an Auto Scaling group in private subnets across two AZs, and RDS Multi-AZ in isolated subnets


Chapter 8 — Cost considerations

Item Approx (us-east-1)
ALB USD 16 / month base + LCU hours
NLB USD 16 / month + NLCU
Data transfer out USD 0.09/GB after 100 GB free
EC2 t3.micro USD 7.50 / month at 24/7 on-demand
NAT gateway USD 32 / month + USD 0.045 / GB processed
RDS db.t4g.micro multi-AZ USD 25-30 / month

A minimal 3-tier shop: ASG of 2 × t3.micro + ALB + RDS Multi-AZ + 1 NAT ≈ USD 100 / month before traffic. Per-AZ NATs add ~USD 32 each for HA.

Watch out: ALB LCU (Load Balancer Capacity Units) is metered on connections, requests, processed bytes, and rule evaluations. A high-traffic ALB can cost more than the EC2 behind it.


Chapter 9 — Beyond EC2 + ASG

Modern compute alternatives to consider for the app tier:

  • ECS Fargate — containers without managing EC2 hosts.
  • EKS — Kubernetes managed control plane.
  • App Runner — push a container, AWS does the rest.
  • Lambda behind ALB — short-lived, infrequent endpoints (Post 05).

EC2 + ASG remains right when you need OS access, GPU, NVMe, or want the lowest level of abstraction. Otherwise consider going up the stack — Post 05 covers serverless, future posts cover containers.


Practice Exercises

Exercise 1 — Two EC2 + ALB by hand

  1. Launch 2 EC2 instances in 2 AZs, each serving a tiny HTTP "hello from <hostname>".
  2. Create a target group; register both.
  3. Create an ALB on port 80 → target group.
  4. Hit the ALB DNS name 10 times; observe both hostnames in responses.
  5. Stop one instance; verify it's marked unhealthy in ≤ 2 minutes and only the survivor receives traffic.

Exercise 2 — Convert to ASG

Tear down Exercise 1; rebuild with:

  • a launch template producing the same web instance,
  • ASG min 2 / desired 2 / max 6 across the same AZs,
  • the target group attached to the ASG,
  • a target-tracking policy on CPU 50%.

Run stress on one instance to push CPU > 70% and watch the ASG add an instance.

Exercise 3 — Host-based routing

Add a second target group with a different /health payload. Add an ALB listener rule:

  • api.<your-domain> → tg-api,
  • default → tg-web.

Then test canary routing with X-Canary: true header.

Exercise 4 — Failure drill

Terminate the ASG's instances manually (aws ec2 terminate-instances). Observe the ASG replace them. Note total recovery time end-to-end.

Exercise 5 — Cost audit

Pull last month's bill for ALB + ASG + NAT + data transfer. Sum the components; identify the largest line item. Suggest one concrete change to shrink it (smaller types, Spot, fewer AZs, S3 endpoint for NAT-bypass).


Cheat-sheet

What How
Pick LB ALB for HTTP, NLB for TCP/UDP/static IP
Target group registered list of instances / IPs / Lambdas
Health check /health shallow, /ready deep
ASG capacity Min / Desired / Max
Multi-AZ list ≥ 2 subnets in --vpc-zone-identifier
Health source use ELB if behind an LB
Scaling policy target tracking → start here
Scheduled for known traffic windows
Instance refresh rolling replace on new launch template
Warm pool pre-stopped instances for fast scale-out
Cooldown prevents oscillation
Cost trap LCU on busy ALB; NAT per-GB; cross-AZ data
3-tier shape ALB (public) → ASG (private) → DB (isolated)
Cert from ACM, attached to ALB listener

What's next

Post 04 covers Managed Data — RDS, Aurora, DynamoDB, ElastiCache. Now that the compute tier can scale and survive, you need a data tier with the same properties.


References

Note: this post is in progress. Planned additions: ALB Cognito auth walkthrough, NLB TLS termination, predictive scaling, blue/green via weighted target groups, mixed-instance ASG with Spot.