A Guide to Three-Tier Web Application Architecture on AWS

Raghu KowshikRaghu Kowshik
16 min read

What’s This Blog All About?

Hey there, reader. This blog is diving deep into setting up a 3-tier architecture for web applications in AWS, focusing on high availability. We’re breaking down what a 3-tier architecture is, its benefits, and how you can leverage AWS services like EC2, RDS, and Load Balancers to build a system that doesn’t buckle under pressure. If you’re looking to create a scalable, secure, and resilient web app, this is for you.

What Is a 3-Tier Architecture?

A 3-tier architecture is a way to structure your web application into three distinct layers: the presentation tier, the application tier, and the data tier. The presentation tier is what users interact with—think web servers handling your front-end. The application tier is the brains of the operation, running your business logic and processing requests. The data tier is where your database lives, storing and managing all your app’s data. In AWS, this might look like EC2 instances for the web and app tiers, with RDS handling the database, all spread across availability zones for redundancy.

What Are the Benefits of a 3-Tier Architecture?

A 3-tier architecture offers several key advantages that make it a preferred choice for building robust web applications. Below are the benefits laid out in clear points for better understanding:

  • Scalability: Each tier—presentation, application, and data—can be scaled independently. For instance, if your web traffic surges, you can add more web servers to the presentation tier without impacting the application or database tiers, ensuring efficient resource allocation.

  • Enhanced Security: By isolating the tiers, a 3-tier architecture minimizes risk. A security breach in one layer, such as the web tier, doesn’t directly expose the application or data tiers, providing an additional layer of protection for sensitive components.

  • Improved Reliability: Distributing the tiers across multiple availability zones ensures high availability. If one zone experiences an outage, the other zones can keep the application running, reducing downtime and maintaining service continuity.

  • Simplified Maintenance: The separation of tiers allows for easier updates and troubleshooting. You can modify or debug one tier, such as applying a patch to the application tier, without affecting the others, streamlining operations and reducing the risk of system-wide disruptions.

  • Better Performance: Each tier can be optimized for its specific role. For example, the presentation tier can focus on delivering fast user experiences, while the application tier handles complex logic, and the data tier ensures efficient data retrieval, leading to overall improved performance.

  • Flexibility and Modularity: The modular design allows you to swap out or upgrade components in one tier without overhauling the entire system. For instance, you can switch to a different database technology in the data tier while keeping the other tiers intact, offering long-term adaptability.

  • Resource Efficiency: By isolating workloads, you can allocate resources more effectively. The presentation tier might need more compute power during traffic spikes, while the data tier might require more storage—3-tier lets you fine-tune resources for each layer, optimizing costs and performance.

This architecture is designed to deliver a balance of performance, security, and operational efficiency, making it ideal for modern web applications.

Steps to Build a Highly-Available 3-Tier Architecture in AWS

This section will guide you through the process of setting up a 3-tier architecture in AWS, ensuring your web application is scalable, secure, and resilient across multiple availability zones.

Part 1 : Creating a VPC and Subnets.

Using the architecture diagram as a reference, we will need to start by creating a new VPC with 2 public subnets and 4 private subnets.

Log into the AWS management console and click the Create VPC button.

We are going to create a VPC with multiple public and private subnets, availability zones, and more, so let’s choose “VPC and more”

Name your VPC. I am using the auto-assigned IPV4-CIDR block of “10.0.0.0/16.” Choose these settings:

  • no IPV6

  • default Tenancy

  • 2 Availability Zones

  • 2 public subnets

  • 4 private subnets

Next, for Nat gateway choose “in 1 AZ,” none for VPC endpoints, and leave the Enable DNS hostnames and Enable DNS resolutions boxes checked.

Before we create the VPC expand the customize AZ’s and customize subnets CIDR blocks tabs.

Click the “Create VPC” button.

The diagram below highlights the route that your new VPC will take.

You will then be shown a workflow chart that shows your resources being created.

View your new VPC after everything is created.

Next click on the Subnets tab in the VPC console. Select one of the new subnets that was created, then under the “Actions” tab, expand the down arrow and select “Edit subnet settings.”

Check “Enable auto-assign IPv4 address” and “Save.” We need to do this for all 6 new subnets that were created.

Update Web Tier Public Route Table

We need to navigate to the route table tag under the VPC dashboard to make sure that the Route table that was automatically created is associated with the correct subnets. Below I highlighted in green the correct public subnets that already associated. If you have none or you only created a stand-alone VPC, you will have to click on “edit subnet associations” and select the ones needed.

Part 2: Creating a Web Server Tier.

Next we will create our first tier that represents our front end user interface (web interface). We will create an auto scaling group of EC2 instances that will host a custom webpage for us. Start by heading to

Next we are going to launch an EC2 instance.

Name your instance and select an AMI. Please use Amazon linux2; I used the updated version and was not able to ssh into it

Select the key pair that you will use, and make sure to select your new VPC and the correct subnet. Auto-assign IP should be enabled.

Create a new security group. For inbound security group rules, add rules for ssh, HTTP, and HTTPS from anywhere. This is not standard or safe, practice, but for this demonstration it is fine.

Leave the configuration storage settings alone. In the Advanced details, head all the way to the bottom. We are going to use a script to launch an Apache web server when the instance starts.

Launch your new instance!

Once your instance is up and running, copy the public IP address and paste it into a web server.

Next I will SSH into the instance to make sure that works as well.

For this project to work we need to create an auto scaling group that we attach to our EC2 instance. This will increase our reliability and availability. Next we are going to create a launch template. Before we do this we will need to define a launch template; this template will outline what resources are going to be allocated when an auto scaling group launches on-demand instances. Under the EC2 dashboard, select Launch Templates, and click the “Create launch template” button.

Name your template, and check the box to “provide guidance.”

Use our recently launched AMI t2.micro instance type and select your key pair.

For the firewall, use “Select existing security group,” and make sure the security group (SG) that we created for the web tier is selected. Under Advanced network configuration, enable Auto-assign public IP.

We are going to leave the storage options alone for now. Click on the Advanced details tab, scroll down, and enter the same script as we did earlier for our EC2 instance.

Click the “Create launch template” button.

Navigate to the autoscaling tab at the bottom of the EC2 dashboard. Click “Create auto scaling group.” The launch template that we just finished creating is the template that our auto scaling group will use to launch new EC2 instances when scaling up.

Name your auto scaling group (ASG), choose the launch template that you created, then click the Next button.

Under Network, make sure to select the VPC that you created earlier, then also under availability zones and subnets select the public subnets that were created; yours may differ.

Click the Next button

Now we are given to option to allocate a load balancer for our ASG. A load balancer will distribute the load from incoming traffic across multiple servers. This helps with availability and performance.

Select “Attach to a new load balancer” and “Application load balancer,” name your load balancer, then select “Internet facing” as this is for our web tier.

your VPC and the two public subnets should already be selected.

Under the “Listeners and routing” section, select “Create a target group,” which should be on port 80 for HTTP traffic.

Leave No VPC Lattice service checked.

Click to turn on Elastic Load Balancing health checks.

Next we configure the group size and scaling policy for our ASG. For reliability and performance, enter 2 for desired capacity and minimum capacity. For maximum capacity, enter 3.

In scaling policies, select “Target tracking scaling policy.” The metric type should be set to “average CPU utilization” with a target value of 50.

Click the Next button.

On the next screen we could add notifications through SNS topics, but I skipped this for now. Click the Next button.

The next screen allows you to add tags which can help to search, filter, and track your ASG’s across AWS. For now just click the Next button.

Review your setting on the next page, and at the bottom click the “Create auto scaling group” button.

After the ASG is finished updating capacity, navigate to your EC2 dashboard to confirm that your new instance has been created.

As you can see, the ASG is doing its job.

Before we move on it is a good idea to take a moment and connect to the instances that were created.

Part 3: Creating an Application Tier.

Next up we are going to create the back-end of our 3-tier architecture. We could create an EC2 instance first, but for this portion I will start by heading to the Launch Templates tab under the EC2 dashboard.

Name your new template, and select the Guidance tab again.

Select browse more AMI’s, then Amazon Linux 2 for your AMI. Select t2.micro for instance type, and also select you keypair.

Under the network setting, we want to limit access to the application tier for security purposes. You don’t want any public access to the the application layer or the data tier after it. We will create a new security group. Select our VPC; I realize now that for this part I could have chosen a better name!

Name your new SG and select the VPC that we created when we started.

We will create 3 security group rule starting with ssh; use My IP as the source. For Security Group 2, use Custom TCP; the source here is the security group from our web tier (tier-1). For the third group, select All ICMP-IPv4, and set the Source type as anywhere. This will allow us to ping our application tier from the public internet to test if traffic is routed properly.

We will again leave the storage volumes alone, and head to the bottom of Advanced details to enter our script. And then click next.

After I had created the launch template I realized I made a couple of mistakes. Instead of modifying the template I decided to start from scratch, but again upon further reflection, in the future I would just modify the template as this would save time, and we all know time is money. The whole reason for these exercises is to learn, right!

I went back to the security group and updated my inbound rules.

Once this was fixed I went back and recreated the application layer template using the ApplicationTierSG1 that I just altered. I just double checked the security group rules and used the same settings for everything else above.

Application Tier Auto Scaling Group

Ok, now we are ready to create our auto scaling group for the application layer. Under the EC2 dashboard go to create an auto scaling group.

Name your new ASG and select the proper launch template, then click the Next button.

Choose the correct VPC and 2 private subnets, then click the Next button.

We are again given the option to attach a load balancer, and we want to do this. Select an application load balancer, name it, and set it as an internal load balancer.Double check that the VPC and subnets are correct. Mine are:

Under “Listeners and routing” create a new target group, and use port 80 once again.

Below I have again chosen to turn on health checks and enable group metrics within CloudWatch.

On the next screen set your desired capacity, minimum capacity, and maximum capacity again.

Then I have chosen to select target tracking with a CPU utilization of 50%.

Click the Next button, add notifications if you want or need and then tags. Review your new ASG settings and create it.

As you can see below, my new application layer ASG is updating the capacity.

Once the new EC2 instances are created and running, we will try to ssh into them. If we set it up correctly, we should not be able to.

When I tried to SSH into the application tier EC2 instance running, the connection timed out, this is exactly what we want here.I also tried to connect using EC2 connect. This failed as well.

We still need to check if our tier-1 servers interact with our tier-2 servers. To test this, you will need to log into your tier-1 EC2 instances via SSH and run a ping command to a private IP address of our tier-2 servers. Below you can see a successful ping.

Update the application (Tier-2) Route table

Head back to the VPC dashboard, select Route tables, and select one of the route tables that was automatically created when we created our VPC. I only have 1 subnet associated with this table so click on Edit subnet associations.

Add another subnet that is private.

Part 4: Created a Database Tier.

Almost there! We have created 2 out of the 3 tiers and tested both successfully.

We are now going to build our database; AWS offers several types of databases but for this exercise we are going to use a MySQL RDS database.

Create a DB Subnet Group

We will begin by creating a subnet groups. Navigate to the RDS console and on the left side menu, click “Subnet Groups” and then the orange “Create DB subnet group.”

For the next part we need to know the availability zones for the last two subnets that were automatically created. Head back to the VPC console. Under Subnets, find the last 2 subnets that you have; make sure not to select the private subnets that you already used in tier 2.Back at the RDS console, select the availability zones that you are going to use.Next up we need to select the proper subnet; the drop down menu only lists the subnet ID. Below is another screenshot of my subnets; the second column is the ID.

Back in the RDS console click Create database.

Select the MySQL DB.

Next you can choose a muli-AZ deployment with 3 database instances. One is a primary instance with two read-only standby instances. This makes for a very reliable system, but we do not need this at this time.

There are also availability and durability options; however, with the free-tier, none are available. We do not need them either.

Under Settings, name your DB and create a master username and password. This username and password these should be different than your AWS account user login, as it is specific to the database you are creating.

You will need your username and password. Make sure to store them in a secure place!

Under Instance configuration, the burstable classes option is is pre-selected because it’s the only one available for the free tier. I left my instance type as adb.t2.micro. You can add storage as needed; I left mine on default settings.

We are going to set up our network manually so choose not to connect to an EC2 resource. Select the proper VPC; the subnet group that you created earlier should be listed as default. Select Create new VPC security group (firewall).

In Database authentication I left the default checked. Click the “Create database” button.

Update the Database Tier Security Group

Navigate over to the VPC console, select Security groups on the left side menu, and then find the database tier security group you just created.

Select the security group that you just created. You need to edit inbound rules; by default the database SG has an inbound rule to allow MySQL/Aurora traffic on port 3306 from your IP address. Delete this rule.

Create a new rule for MYSQL/Aurora on port 3306: for the Source, select Custom to add your security group for your application layer (tier-2 SG).

Update Tier 3 Private Route Tables

In the last step for our database tier, we need to make sure that the route table we associate with our databases private subnets has both subnets listed in subnet associations. If not, add the other subnet, and save.Our three tier architecture is done! We have already tested our web and application layers, but we are going to go a step futher here.

Part 5: Testing.

We can’t directly SSH to the database, but we can use an SSH forwarding agent to achieve this.

You need to add your access key pair file to your keychain. To do this, first make sure you are in your local host (use the command exit to get out of any EC2 instance you’re connected to). Then use the following command:

Now that your key pair file is added to your keychain, the SSH agent will scan through all of the keys associated with the keychain and find your matching key.

Now reconnect to the web tier EC2; however, this time use -A to specify you want to use the SSH agent.

Once you are logged back into your tier-1 EC2, use the following command to check if the SSH agent forwarded the private key.

Our key pair has been forwarded to our public instance. Go copy your tier-2 application layer private IP address and copy it into the next command.

We have now SSH’ed from your public tier 1 web instance into your private tier 2 application instance!

Testing Connectivity to the Database Tier

There are a few ways you can connect to your RDS database from your application tier. One way is to install MySQL on your private tier 2 instance to access your database. We are going to utilize this method. While logged into your application tier instance, use this command:

This command installs the MariDB package, which is used to read MySQL. Once installed, you should be able to use the following command to log into your RDS MySQL database. You will need your RDS endpoint, user name, and password. To find your RDS database endpoint, navigate to the database you created and find the endpoint under Connectivity & Security.

We have now successfully connected to our MySQL database from the application tier. We have connectivity with all of our tiers!

I hope that this tutorial was helpful to some of you. Please remember to go back and delete the resources that cost money. Stop your Ec2 instances and delete your NAT gateway, elastic IP, and ALBs. This is a good practice to get into to prevent running up unnecessary costs. Good luck!

Thank You!

0
Subscribe to my newsletter

Read articles from Raghu Kowshik directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Raghu Kowshik
Raghu Kowshik