🌐 Deploying a 3-Tier Web Application Architecture on AWS using VPC

Gujjar ApurvGujjar Apurv
8 min read

In this project, I have designed and deployed a 3-Tier Web Application architecture within a custom Virtual Private Cloud (VPC) using AWS services. This architecture follows industry best practices for security, scalability, and separation of concerns

πŸ”’ Secure | βš™οΈ Modular | ☁️ AWS-Powered

In this blog, I’ll demonstrate how I deployed a 3-tier application on AWS using custom VPC. This architecture includes:

  • Nginx as a Reverse Proxy (Web Layer)

  • Apache Tomcat as the Application Server

  • MySQL as the Database Server

🧱 What is 3-Tier Architecture?

A 3-tier architecture separates the app into:

  1. Web Tier (Nginx) – Handles incoming HTTP requests

  2. App Tier (Tomcat) – Runs backend application logic

  3. DB Tier (MySQL) – Stores application data

πŸ”§ Tech Stack & AWS Services Used

LayerComponentAWS Service
WebNginxEC2 in Public Subnet
AppTomcatEC2 in Private Subnet
DBMySQLEC2 in Private Subnet
NetworkVPC, Subnets, Route TablesAWS VPC
OthersNAT Gateway, IGW, SGsAWS Infra

πŸ—ΊοΈ High-Level Architecture Diagram

This setup includes:

  • 1 Public Subnet for Nginx

  • 2 Private Subnets: App Tier (Tomcat) and DB Tier (MySQL)

  • Security Groups with limited, directional access

  • NAT Gateway for outbound internet access from private subnets

πŸͺœ Step-by-Step Implementation

βœ… Step 1: Create VPC

  • CIDR Block: 10.1.0.0/16

βœ… Step 2: Create Subnets

  • 10.1.1.0/24 – Public (Web: Nginx)

  • 10.1.2.0/24 – Private (App: Tomcat)

  • 10.1.3.0/24 – Private (DB: MySQL)

βœ… Step 3: Setup Internet Gateway + NAT

  • IGW for public subnet (Nginx)

  • NAT Gateway for public subnets (Web)

βœ… Step 4: Configure Route Tables

  • Public Route Table: 0.0.0.0/0 β†’ IGW

  • Private Route Table: 0.0.0.0/0 β†’ NAT

βœ… A. Public Route Table Configuration

  • I created a Route Table named web-rt.

  • I associated the public subnet (used for Nginx) with this web-rt.

  • Then, I edited the route in web-rt:

    • Destination: 0.0.0.0/0 (this allows internet traffic)

    • Target: Internet Gateway (attached to the VPC)

  • This allows public instances like Nginx server to access the internet directly.

πŸ”’ B. Private Route Table Configuration

  • I created another Route Table named private-rt.

  • I associated both private subnets (one for Tomcat app and one for MySQL DB) with this private-rt.

  • Then, I edited the route in private-rt:

    • Destination: 0.0.0.0/0

    • Target: NAT Gateway (deployed in the public subnet)

  • This setup allows private instances to access the internet only for updates (e.g., apt install), without being exposed to incoming public traffic.

βœ… Step 5: Launch EC2 Instances

🌍 Web Tier (Nginx)

  • EC2 in public subnet

  • Installed Nginx

  • Acts as Reverse Proxy forwarding to Tomcat

  • SG allows ports: 80 (HTTP) and 8080 (proxy)

πŸ›‘ Bastion Host

  • EC2 in public subnet for SSH access to private EC2s

  • SG allows port: 22

βš™ App Tier (Tomcat)

  • EC2 in private subnet

  • Installed Apache Tomcat

  • SG allows traffic only from Nginx EC2 (Web SG)

πŸ’Ύ DB Tier (MySQL)

  • EC2 in private subnet

  • Installed MySQL, secured

  • SG allows port 3306 only from App Server

πŸ” Step 3: Private Server Access via Public EC2 (Jump Server Method)

Since App and DB servers are in private subnets, I used the Web Server (Public EC2) as a jump host to access them.

Steps Followed:

  1. SSH into Public Web Server using .pem file:

     ssh -i "my-key.pem" ubuntu@<public_ip>
    
  2. Created a new file using:

     vim jump.pem
    
  3. Pasted the private key of the internal servers (App/DB) in jump.pem.

  4. Changed permission:

     chmod 400 jump.pem
    
  5. Then, from the public server, logged in to private server:

     ssh -i jump.pem ubuntu@10.1.2.97  # App Server
     ssh -i jump.pem ubuntu@10.1.3.105 # DB Server
    

βœ… This way, I securely accessed private servers using the public EC2 as a jump point.

🌐 Nginx Installation on Web EC2 Instance (Public Subnet)

After launching the Web EC2 instance (in the public subnet), I connected to it using SSH with the default Ubuntu user. Then I installed and configured Nginx as follows:

πŸ”§ Steps Performed:

  1. SSH into EC2 Instance:

     ssh -i "keypair.pem" ubuntu@<Public-IP>
    
  2. Update the System Packages:

     sudo apt update -y
    
  3. Install Nginx Web Server:

     sudo apt install nginx -y
    
  4. Start the Nginx Service:

     sudo systemctl start nginx
    
  5. Enable Nginx to Start on Boot:

     sudo systemctl enable nginx
    

πŸš€ Tomcat Installation on App EC2 Instance (Private Subnet)

On the App Server EC2 instance (launched in the private subnet), I installed and started Apache Tomcat to host Java-based web applications.

Since Tomcat requires Java, I installed JDK first, then downloaded and configured Tomcat.

πŸ”§ Steps Performed:

  1. Update System Packages:

     sudo apt update -y
    
  2. Install Java Development Kit (Required for Tomcat):

     sudo apt install default-jdk -y
    
  3. Download the Latest Tomcat (Version 11.0.9):

     wget https://downloads.apache.org/tomcat/tomcat-11/v11.0.9/bin/apache-tomcat-11.0.9.tar.gz.asc
    
  4. Extract the Downloaded Archive:

     tar -xvzf apache-tomcat-11.0.9.tar.gz
    
  5. Start the Tomcat Server:

     ls
     cd apache-tomcat-11.0.9.tar.gz
     ls
     cd bin
     ./startup.sh
    

πŸ›’οΈ MySQL Installation on DB EC2 Instance (Private Subnet)

On the Database Server EC2 instance (placed in the private subnet), I installed MySQL Server to manage the backend database of the application securely.

πŸ”§ Steps Performed:

  1. Update System Packages:

     sudo apt update -y
    
  2. Install MySQL Server:

     sudo apt install mysql-server -y
    
  3. Start and Enable MySQL Service:

     sudo systemctl start mysql
     sudo systemctl enable mysql
    

βš™οΈ MySQL Configuration on DB EC2 (Private Subnet)

After installing MySQL on the DB Server (private subnet), I performed additional configuration to allow internal app server access by setting the bind-address to the server’s private IP.

πŸ” Step 1: Login to MySQL as Root User

To securely access MySQL, I logged in using the root user:

sudo mysql -u root -p

(You’ll be prompted to enter the root password set during secure installation.)


πŸ› οΈ Step 2: Edit MySQL Configuration File

I modified the MySQL bind-address to allow access from the app server (within the VPC):

sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf

Inside this file, I located the following line:

bind-address = 127.0.0.1

And changed it to my DB EC2’s private IP, for example:

bind-address = 10.0.2.15

βœ… This step ensures MySQL accepts connections only from internal sources (e.g., the app server), not from the public internet β€” keeping the database secure.


πŸ”„ Step 3: Restart MySQL to Apply Changes

sudo systemctl restart mysql

πŸ”— Network Connectivity Testing (Ping & Telnet)

To ensure all the instances in my 3-tier architecture (Web, App, and DB) are properly connected and communicating within the VPC, I performed two essential network checks:


βœ… 1. Ping Test (Initial Connectivity Verification)

Before running any application-level commands, I verified the basic connectivity between all EC2 instances using ping.

  • From Web (10.1.1.44):

    • Ping to App server (10.1.2.97)

    • Ping to DB server (10.1.3.105)

  • From App (10.1.2.97):

    • Ping to Web server (10.1.1.44)

    • Ping to DB server (10.1.3.105)

  • From DB (10.1.3.105):

    • Ping to Web server (10.1.1.44)

    • Ping to App server (10.1.2.97)

πŸ“ All ping tests were successful, confirming that the subnet routing and security group rules were correctly configured for basic communication.

βœ… Verifying Internal Connectivity using Telnet in 3-Tier Architecture

To ensure all components in the 3-Tier Architecture (Web, App, and DB) can communicate with each other, we use the telnet command to test port-level connectivity.

Below is how each instance should verify connection with others using Telnet:


πŸ”Ή From Web Server (Public Subnet - IP: 10.1.1.44)

  • Check connectivity to App Server (Tomcat):

      telnet 10.1.2.97 8080
    
  • Check connectivity to Database Server (MySQL):

      telnet 10.1.3.105 3306
    

πŸ”Ή From App Server (Private Subnet - IP: 10.1.2.97)

  • Check connectivity to Web Server:

      telnet 10.1.1.44 22
    
  • Check connectivity to Database Server:

      telnet 10.1.3.105 3306
    

πŸ”Ή From DB Server (Private Subnet - IP: 10.1.3.105)

  • Check connectivity to Web Server:

      telnet 10.1.1.44 22
    
  • Check connectivity to App Server:

      telnet 10.1.2.97 8080
    

βœ… If Telnet successfully connects (i.e., blank screen or "Connected"), it confirms that the port is open and reachable from that instance.

❌ If Telnet fails (i.e., "Connection refused" or "Unable to connect"), check Security Groups, NACLs, and Routing Tables.

πŸ“ Conclusion

This project demonstrates a successful deployment of a secure and scalable 3-tier architecture on AWS using Nginx (Web), Tomcat (App), and MySQL (DB).
It follows best practices for network isolation, access control, and modular application deployment in a cloud environment.

πŸ‘¨β€πŸ’» About the Author

This project is a deep dive into the AWS ecosystem designed to strengthen my foundation in cloud-native architecture, automation, and service integration using only AWS services.

This series isn't just about using AWS; it's about mastering the core services that power modern cloud infrastructure.


πŸ“¬ Let's Stay Connected


πŸ’‘ If you found this project useful, or have any suggestions or feedback, feel free to reach out or drop a comment I’d love to connect and improve.
This is just the beginning many more builds, deployments, and learnings ahead.

1
Subscribe to my newsletter

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

Written by

Gujjar Apurv
Gujjar Apurv

Gujjar Apurv is a passionate DevOps Engineer in the making, dedicated to automating infrastructure, streamlining software delivery, and building scalable cloud-native systems. With hands-on experience in tools like AWS, Docker, Kubernetes, Jenkins, Git, and Linux, he thrives at the intersection of development and operations. Driven by curiosity and continuous learning, Apurv shares insights, tutorials, and real-world solutions from his journeyβ€”making complex tech simple and accessible. Whether it's writing YAML, scripting in Python, or deploying on the cloud, he believes in doing it the right way. "Infrastructure is code, but reliability is art."