Skip to content

Primary SAA curriculum

Public Web App On AWS

A highly available public web application with one public entrance, private application servers, managed state, and health-based scaling.

23 min read

After this, you will understand

Learn the reusable AWS pattern behind many AWS Solutions Architect Associate exam (SAA-C03) questions: one public entrance, replaceable private compute, and durable managed state.

Article guideprerequisites, mental models, and concepts

Article overview

foundationCloudCertificationNetworking

Three useful mental models

In plain terms

Users reach a public load balancer, the load balancer sends requests to healthy private application instances, and those instances use private databases and managed storage.

Decision pressure

One public server becomes the entry point, application, file store, database, credential store, and single failure boundary.

Exam-ready model

Separate the web edge, application tier, and data tier; spread capacity across Availability Zones; then use health checks, Auto Scaling, IAM roles, RDS, and S3 for their specific jobs.

Think before reading

If the application is public, which part actually needs to accept traffic from the internet?

Usually the internet-facing Application Load Balancer. The EC2 application instances and database can remain private behind it.

Connected learning

These lessons add useful context to the current core lesson.
  1. 1CloudFront WAF Protected Web EdgeAWS Scenario

Concepts Covered

  • Dynamic public web applications
  • Regions, Availability Zones, and subnet tiers
  • Internet-facing Application Load Balancers
  • Listeners, target groups, and health checks
  • Private EC2 instances in an Auto Scaling group
  • Stateless application servers
  • Private RDS Multi-AZ databases
  • S3 object storage and presigned uploads
  • EC2 instance roles and temporary credentials
  • Security group chaining
  • NAT gateways versus inbound application traffic
  • CloudWatch metrics, alarms, and logs
  • AWS Solutions Architect Associate exam (SAA-C03) recognition patterns

1. Situation

Your team is launching an online store. Unlike a purely static website, this application must run server-side code for each request.

For example, when a user opens /orders/42, the application must:

  1. identify the user;
  2. read order data from a relational database;
  3. apply business rules; and
  4. generate a response.

Users can also upload product images and receipts. Traffic is usually moderate, but promotions can create sudden spikes. The business cannot accept an outage every time one virtual machine or one Availability Zone has a problem.

The requirements are:

  • a public HTTPS domain;
  • no direct internet access to application servers or the database;
  • enough capacity when traffic grows;
  • continued service when one application instance fails;
  • continued service when one Availability Zone has a problem;
  • durable storage for relational data and uploaded files; and
  • no long-lived AWS access keys stored on servers.

This study develops the foundational AWS pattern:

public entry point -> private application tier -> private data tier

The goal is not to memorize a diagram. It is to understand which problem forces each layer to exist.

2. Naive Design

The team begins with one EC2 instance that has a public IP address:

One server is the application, data store, file store, session store, and credential boundary, so one server failure affects every job.

The instance does everything:

  • accepts HTTPS requests;
  • runs the application;
  • stores uploaded files on its local disk;
  • runs the database;
  • stores user sessions in memory; and
  • keeps AWS access keys in a configuration file.

This resembles a familiar virtual private server. It is easy to understand, inexpensive at small scale, and reasonable for an early experiment.

Its simplicity comes from combining every responsibility into one failure boundary.

Adding a larger instance postpones that problem. It does not remove it.

3. What Breaks

Follow four failure stories.

Failure 1: the server stops

At 10:00, the instance fails a system status check. The application, database, uploaded files, and in-memory sessions all disappear from service together.

There is no second healthy application target and no component able to route around the failure.

The architecture needs multiple application instances and health-based routing.

Failure 2: a promotion creates a traffic spike

At 14:00, request volume increases fivefold. The single server reaches its CPU and connection limits. Requests become slow, then begin timing out.

Manually resizing the instance requires prediction and intervention. The architecture needs horizontal scaling: adding and removing application instances as demand changes.

Failure 3: the team adds a second server

The second instance helps with compute, but state now splits:

user A uploads image.jpg -> instance A local disk
next request             -> instance B cannot find image.jpg

The same problem affects in-memory sessions. Replaceable servers cannot be the only owners of durable or shared state.

The architecture needs state outside the application instances.

Failure 4: public exposure grows with every component

The server accepts user traffic, SSH, and database connections. Long-lived AWS keys sit on disk. A mistake in one security rule can expose far more than the web application.

The architecture needs a narrow public boundary and temporary workload credentials.

These failures lead to four design moves:

one public entrance
+ multiple replaceable app instances
+ managed durable state
+ identity without stored access keys
Each failure in the naive design motivates a specific architecture change; the services are added to solve those problems, not merely to make the diagram larger.

4. AWS Architecture

Build the architecture one responsibility at a time.

Step 1: create network boundaries across two Availability Zones

A VPC is the application's private network boundary in an AWS Region. An Availability Zone, or AZ, is an isolated location within that Region.

Use at least two AZs so one location is not the only place able to run the application.

Within each AZ, create subnet tiers:

A VPC spans Availability Zones, while each subnet belongs to one AZ. Running real capacity in both AZs creates redundancy.

A public subnet has a route to an internet gateway. A private subnet does not have a direct route to the internet gateway. The label comes from routing, not from the resource's name. The route alone does not give an EC2 instance a public IP address; routing and public addressing are separate requirements for direct internet connectivity.

Before using those labels, establish the routing model:

  • A route table is a set of rules associated with a subnet. It decides where network traffic from that subnet should go next.
  • A route's destination describes which destination IP addresses the rule matches.
  • A route's target is the next network component for matching traffic.
  • An internet gateway is the VPC component that provides a path between public VPC resources and the internet.
  • 0.0.0.0/0 represents every IPv4 destination, so it acts as the general anything else route.

A simplified public-subnet route table contains this rule:

DestinationTargetMeaning
0.0.0.0/0Internet gatewaySend general IPv4 internet traffic to the VPC's internet gateway.

A private subnet does not use that direct internet-gateway route. It may later use a NAT gateway for initiated outbound traffic, but that still does not make it a public entrance. A real route table also contains a local route for traffic inside the VPC; the table above isolates the rule that makes the public/private distinction easier to see.

Routing creates the public/private distinction. NAT can carry connections that a private instance starts, but it does not make that instance a public inbound destination.

The public subnets will hold the internet-facing entry layer. Application and database resources will use the private tiers.

Step 2: add one public entry point

Create an internet-facing Application Load Balancer (ALB) in public subnets across at least two AZs.

The ALB understands HTTP and HTTPS. Its listener waits for traffic on a port such as 443. A listener rule forwards matching requests to a target group, which is the set of application backends eligible to receive them.

The important configuration relationship is:

ComponentExample configurationJob
HTTPS listenerPort 443 with an AWS Certificate Manager (ACM) certificateAccept encrypted public requests.
Listener ruleForward matching requests to the application target groupChoose the backend group for the request.
Target groupEC2 application instances on the application portHold the possible request destinations.
Health checkRequest a path such as /healthDecide which registered targets are currently eligible.
Route 53 answers DNS. The browser then connects to the ALB, whose listener rule selects a target group and forwards the request to a healthy application target.

Attach the ACM certificate to the HTTPS listener. Because an ALB is regional, its ACM certificate is created in the same Region as the load balancer.

Route 53 only answers DNS. It tells the browser how to find the ALB; it is not an HTTP hop through which every request passes.

Step 3: make the application tier private and replaceable

Launch EC2 application instances in the private app subnets through an Auto Scaling group.

The Auto Scaling group uses a launch template to describe each new instance: its machine image, instance type, security groups, IAM role, and startup configuration. It also maintains a chosen minimum, desired, and maximum capacity.

The ALB routes around failure, the Auto Scaling group restores desired capacity, and a scaling policy changes capacity when demand changes.

The ALB sends traffic only to targets that pass the target group's health check. When Elastic Load Balancing health checks are enabled for the Auto Scaling group, the group can replace an instance that the load balancer reports as unhealthy.

These are related but different jobs:

  • the ALB routes around an unhealthy instance;
  • the Auto Scaling group restores capacity by launching a replacement; and
  • a scaling policy changes capacity when load changes.

The private EC2 instances do not need public IP addresses to receive requests from the ALB. The public connection ends at the ALB. The ALB then opens a separate connection to a healthy target's private IP address inside the VPC, and the application security group allows that connection from the ALB security group.

Step 4: move durable state out of EC2

Place the relational database in private database subnets. First, create an RDS DB subnet group: a list of the subnets where RDS is allowed to place database resources. Include private database subnets from multiple AZs so RDS has more than one placement location. The subnet group creates placement options; it does not create a standby by itself. When the database must survive an AZ failure, enable a Multi-AZ DB instance deployment so RDS also maintains a synchronous standby in another AZ.

Store uploaded objects in S3 rather than on EC2 instance disks.

Replaceable application instances keep durable state in RDS and S3, so any healthy instance can handle the next request.

Multi-AZ protects database availability; its standby is not the read-scaling target in this classic deployment. Backups and point-in-time recovery are still needed because a standby can receive bad data changes as well as good ones.

Once shared state lives in RDS and S3, any healthy application instance can handle the next request. This is the practical meaning of a stateless application tier: an instance is not the only durable owner of data needed by future requests.

For large uploads, the application can create a short-lived S3 presigned URL. The browser then uploads a specific object directly to S3 without receiving AWS credentials and without sending the file bytes through EC2. The URL carries a signature created with the application's AWS identity, so S3 can authorize that limited operation without giving the browser general AWS credentials. Because anyone holding the URL can use it until it expires, keep its operation, object key, and lifetime narrow.

Step 5: give the workload an identity

An IAM role is the AWS identity the application uses while it runs. The role's permissions define which AWS API operations that identity may perform. Because this application runs on EC2, attach the role through an instance profile, which is the AWS mechanism that makes the role available to an EC2 instance. The application can then obtain rotating temporary credentials automatically instead of reading long-lived access keys from a file.

Grant only the required actions and resources. An action is an operation such as reading an S3 object; a resource identifies what that operation may affect, such as one S3 bucket prefix or one Secrets Manager secret.

An IAM role answers:

what AWS APIs may this application call?

A security group answers:

which network connections may reach this resource?

Do not confuse identity permissions with network reachability. A role that permits s3:PutObject does not create a network route to S3, and a route to S3 does not grant permission to upload.

IAM answers what AWS API operations the workload may perform. Security groups answer which network connections may reach a resource.

Step 6: add outbound access only where it is needed

Private application instances may need outbound access for operating system updates or external APIs. A public NAT gateway can provide outbound IPv4 access while preventing the internet from initiating connections to those instances.

NAT is not part of the user's inbound request path:

DirectionPathWhat the first public component does
Inbound user requestUser → internet-facing ALB → private EC2The ALB accepts the public request and forwards it privately.
Outbound application requestPrivate EC2 → NAT gateway → internet gateway → internetNAT translates the instance's private source address for a connection the instance initiated.

The NAT gateway does not accept unsolicited inbound user traffic and does not sit between the user and the ALB.

The ALB is the public inbound entrance. NAT is an outbound path for connections initiated by private resources.

For S3 and DynamoDB traffic, a gateway VPC endpoint can provide a private route and avoid sending that traffic through a NAT gateway.

The completed architecture is:

The ALB is the public entrance, EC2 is private and replaceable, and RDS and S3 keep durable state outside the application instances.

5. Request Or Data Flow

Learn three lifecycles instead of memorizing every arrow at once.

Lifecycle 1: normal application request

  1. Route 53 answers the DNS query for app.example.com with the ALB target.
  2. The browser opens an HTTPS connection to the ALB.
  3. The ALB listener evaluates its rules and selects the target group.
  4. The ALB sends the request to one healthy EC2 target over private networking.
  5. The application reads or writes relational data through the RDS endpoint.
  6. The application returns the response through the ALB to the user.
DNS resolution happens first; the request then travels through the ALB to a healthy application instance and its database dependency.

Lifecycle 2: an application instance fails

  1. The target stops returning the expected health-check response.
  2. The ALB marks it unhealthy and stops routing new requests to it.
  3. Healthy targets in the other AZ continue receiving traffic.
  4. If ELB health checks are enabled on the Auto Scaling group, the group replaces the unhealthy instance.
  5. The replacement starts from the launch template, passes health checks, and enters service.
The ALB preserves service first by avoiding the failed target; Auto Scaling restores the missing capacity afterward.

Health checks should test whether the application can serve useful traffic. A check that returns success while the application cannot operate gives the load balancer false confidence. Avoid making the check so dependent on every downstream system that one database issue causes every app target to be replaced simultaneously.

Lifecycle 3: a user uploads a file

For a simple upload, the application receives the file and writes it to S3 using its instance role.

For a larger direct upload:

The application decides who may upload and signs a limited request; the browser sends the file bytes directly to S3 without receiving general AWS credentials.

The application still decides who may upload and what object key is allowed. S3 carries the file bytes.

6. Security Controls

Chain security groups by tier

Use security group references to express which tier may call the next tier:

Security groupAllow inbound fromTypical purpose
ALB security groupInternet clients on HTTPSPublic application entry
Application security groupALB security group on the application portPrevent direct client access to EC2
Database security groupApplication security group on the database portPrevent direct client and ALB access to RDS
Each tier admits connections only from the tier immediately before it, which keeps EC2 and RDS off the direct public path.

This is more precise than allowing the entire VPC CIDR at every layer.

Keep application and database resources private

Do not assign public IP addresses to normal application instances. Do not make RDS publicly accessible. The fact that the product is public does not make every component public.

Use roles for AWS API access

Attach a least-privilege instance role rather than storing access keys in the AMI, source code, user data, or environment files managed by hand.

Store application secrets separately

An EC2 role proves the application's AWS identity. A database password is still a secret. Store secrets in a managed service such as Secrets Manager and grant the role permission to retrieve only the required secret.

Protect administrative access

Avoid opening SSH to the world. Prefer Systems Manager Session Manager or tightly controlled administrative paths when interactive access is required.

Encrypt the intended paths

Use HTTPS from users to the ALB. Use encryption for RDS, S3, EBS, backups, and application-to-database connections when the security requirement calls for it.

7. Resilience Controls

Run real capacity in more than one AZ

Selecting two subnets is not enough if the Auto Scaling group runs only one instance. To tolerate one instance or AZ failure without losing all application capacity, maintain healthy instances across multiple AZs and enough spare capacity for the remaining AZ to carry the load.

Combine routing with replacement

The ALB removes unhealthy targets from service. Auto Scaling replaces failed capacity. Neither action repairs application bugs, so monitor whether a new release causes every target to fail the same health check.

Keep compute replaceable

Do not make local files or in-memory sessions the only copy of important state. Use RDS, S3, DynamoDB, ElastiCache, or another suitable shared service according to the data's durability and consistency requirements.

Protect the database separately

Use RDS Multi-AZ for managed failover when database availability matters. Use automated backups and point-in-time recovery for historical recovery. These solve different problems.

Review Highly Available RDS App for the database failover lifecycle and application reconnect behavior.

Avoid moving the failure boundary to NAT

If private instances need internet access during an AZ failure, the NAT design must survive that failure too. A single NAT gateway runs in one AZ. If application subnets in other AZs all depend on it, losing its AZ also removes their internet egress even when their EC2 instances remain healthy.

Monitor the user path, not only the servers

Use CloudWatch metrics and alarms for signals such as ALB response time and 5XX errors, healthy target count, Auto Scaling capacity, EC2 resource pressure, and RDS connections and storage. Centralize application logs so a terminated instance does not take its only diagnostic history with it.

An alarm should describe an action-worthy symptom. For example, low healthy-target count reveals lost serving capacity more directly than an alarm that reports only one instance's CPU.

8. Performance Controls

Use a scaling metric that represents application pressure. Useful signals may include:

  • ALB request count per target;
  • CPU utilization for compute-heavy work;
  • queue depth for worker-style processing; or
  • a custom latency or concurrency metric.

CPU is not automatically the best metric. An application can run out of database connections or request workers while CPU remains moderate.

Set a realistic instance warm-up period so a newly launched instance has time to start before scaling decisions treat it as fully ready.

Move static assets and public downloads away from the application tier when useful. S3 and CloudFront can deliver those objects without consuming EC2 request capacity. The next study, CloudFront WAF Protected Web Edge, develops that global edge.

Tune the database only after identifying the bottleneck. Indexing, connection pooling, instance sizing, caching, and read replicas solve different pressures. A read replica helps read scale; it is not the classic automatic Multi-AZ failover answer.

9. Cost Controls

Set deliberate Auto Scaling minimum, desired, and maximum capacity. A minimum that is too low may violate availability; a minimum that is too high wastes money.

Right-size EC2 and RDS using real metrics. Use Savings Plans or reservations for predictable baseline usage, and use Spot Instances only for capacity that can tolerate interruption.

Watch NAT gateway processing and cross-AZ data-transfer patterns. Use an S3 gateway endpoint when it meets the access pattern so private S3 traffic does not need NAT.

Store uploaded files in S3 rather than growing every application instance's EBS volume. Apply lifecycle rules when older objects or logs can move to cheaper storage classes or expire.

Do not add Multi-AZ, read replicas, caches, CloudFront, and multiple NAT gateways merely because they appear in a reference diagram. Each adds value only when a requirement justifies its availability, performance, security, or operational benefit.

10. Exam Variants

If the question says...Think...Why
"Highly available web application"ALB + Auto Scaling targets across multiple AZsThe ALB routes around failures while Auto Scaling maintains capacity
"Only the load balancer should be public"Internet-facing ALB in public subnets; EC2 in private subnetsPublic ingress and private execution are separate tiers
"Instances need S3 access without stored credentials"EC2 instance roleThe workload receives temporary credentials automatically
"Database must not be internet accessible"Private database subnets + database security groupOnly the application tier needs database network access
"Automatic relational database failover"RDS Multi-AZA standby is maintained for availability
"Scale read-heavy database traffic"Read replicaRead scaling is different from Multi-AZ failover
"Uploaded files disappear when instances are replaced"Store objects in S3Application instances should not own the only durable copy
"Private instances need software updates from the internet"NAT gateway for outbound accessNAT permits initiated outbound connections, not inbound user traffic
"Private instances need only S3 or DynamoDB"Gateway VPC endpointThose service paths can avoid NAT
"Global users need cached content and edge protection"Add CloudFront and, when required, AWS WAFThis evolves the regional public edge

Reconstruct the base design from the requirements:

one public web entrance       -> internet-facing ALB
healthy dynamic compute      -> target group + EC2 Auto Scaling
no direct server exposure    -> private application subnets
relational durable state     -> private RDS
uploaded object state        -> S3
AWS API permissions          -> EC2 instance role
AZ failure tolerance         -> real capacity across multiple AZs

11. Common Traps

TrapBetter reasoning
"The app is public, so EC2 must have a public IP."The ALB is public and reaches EC2 over private VPC addresses.
Drawing user -> Route 53 -> ALB as the HTTP pathRoute 53 answers DNS; the user then connects to the ALB.
Sending inbound users through a NAT gatewayNAT provides outbound connectivity for private resources; it is not the public application entrance.
Running one instance while selecting two AZsSubnet selection alone does not create redundant application capacity.
Assuming the ALB replaces failed serversThe ALB routes around failure; Auto Scaling replaces capacity when configured to use the relevant health signal.
Saving uploads or sessions only on EC2Replaceable instances require shared or durable state outside the instance.
Storing AWS keys on the serverUse an instance role with temporary credentials.
Allowing the entire internet to reach the application portLet the app security group accept that port only from the ALB security group.
Using a read replica for automatic failoverClassic Multi-AZ and read replicas solve availability and read-scaling requirements respectively.
Treating Multi-AZ as a backupSynchronously copied mistakes still require backup recovery.
Making the health check depend on every downstream serviceA shared downstream failure can make all targets unhealthy and trigger harmful replacement churn.

Final Mental Model: One-Minute Review

Final Architecture Map

Use this map to consolidate the boundaries: the ALB is public, the application instances are private and replaceable, and durable state lives outside EC2.

TermExact jobRemember it as
Route 53Answers DNS so the client can find the public entry pointThe address book
Application Load BalancerAccepts public HTTP/HTTPS traffic and routes it to healthy targetsThe reception desk
Target group and health checksTrack which application instances are ready to receive requestsThe healthy-worker roster
Auto Scaling groupMaintains and adjusts the number of EC2 application instancesThe staffing manager
Private EC2 instancesRun the server-side application without accepting direct internet trafficWorkers in private offices
RDS and S3Keep relational data and uploaded objects outside replaceable computeThe records room and warehouse
IAM roleGives the application temporary permission to call AWS APIsThe worker's job badge
Security groupsControl which network connections may enter each tierThe locks between rooms
Remember the architecture as one public entrance, private replaceable compute, and durable state outside the instances.

The core pattern is one public entrance, replaceable private compute, and durable state outside the instances. The ALB routes around failure; Auto Scaling replaces capacity; IAM roles authorize AWS API calls; security groups control network paths.

Review Public vs Private Subnets for the routing boundary, ALB vs NLB vs GWLB for load-balancer selection, Amazon EC2 Auto Scaling for capacity behavior, Amazon RDS for managed relational databases, Amazon S3 for durable object storage, and Amazon CloudWatch for metrics, alarms, and logs.

Continue to CloudFront WAF Protected Web Edge to evolve this regional architecture into a global cached and protected entry point.

Official AWS references:

Finished reading?

Your reading history is saved in this browser so you can continue later.

Recommended Next

CloudFront WAF Protected Web EdgeAWS Architecture Scenarios20 min read

This applies the foundation mental models to a real architecture decision instead of a service inventory.

Optional exploration

These links add context, but they do not replace the recommended next lesson.

Arcflow Plus is coming — review drills, research breakdowns, more AI. Get one email at launch.