Back to projects

Engineering case study

Terraform · AWS · Three-tier architecture

Book Review: a modular three-tier AWS foundation

A Terraform scaffold that separates a Next.js frontend, Node.js API, and MySQL database across public and private network tiers, with controlled traffic paths between each layer.

TerraformAWSEC2RDS MySQLALBNginx
Infrastructure repository No permanent live demo

5

Terraform modules

6

purpose-built subnets

3

isolated application tiers

2

application load balancers

The problem

What needed to be solved

The application needed more than a single public server. Web traffic, application traffic, and database access had to follow explicit paths while the infrastructure remained understandable enough to reproduce and extend.

My role

What I owned

I designed the Terraform module boundaries, network topology, security-group relationships, load-balancer routing, EC2 bootstrap automation, and the RDS integration. The focus was infrastructure and deployment automation for an existing application codebase.

Architecture

How the system fits together

Traffic enters a public load balancer, reaches the web tier, crosses an internal load balancer to the private API tier, and reaches MySQL through a database-only security-group rule.

Key decisions

Engineering choices and trade-offs

1

Module boundaries follow infrastructure concerns

Networking, security, compute, load balancing, and database resources live in five focused modules. Outputs form explicit contracts between them.

2

Security groups describe the traffic graph

The web tier accepts traffic from the public ALB, the API accepts traffic from the internal ALB, and MySQL accepts traffic only from the API security group.

3

Private tiers retain controlled outbound access

A NAT gateway supports package installation and application bootstrapping without assigning public addresses to the API or database tiers.

4

Instances bootstrap from infrastructure outputs

Terraform templates inject ALB and RDS endpoints into user-data scripts, reducing manual configuration after provisioning.

Terraform snapshot

Infrastructure outputs are wired into compute bootstrapping

The root module composes the five infrastructure concerns and passes generated load-balancer and database endpoints into each EC2 tier.

Inspect the source file
main.tf
module "ec2" {
  source = "./modules/ec2"

  web_user_data = templatefile("scripts/frontend-userdata.sh.tpl", {
    public_alb_dns  = module.alb.public_alb_dns_name
    private_alb_dns = module.alb.private_alb_dns_name
  })

  app_user_data = templatefile("scripts/backend-userdata.sh.tpl", {
    db_host = split(":", module.database.db_endpoint)[0]
    db_name = var.db_name
  })
}
Code excerpt from main.tf

Measurable outcome

What the implementation demonstrates

  • One Terraform root expresses five reusable modules, six subnets across two availability zones, two application load balancers, two compute tiers, and one managed database.
  • The API and database have no direct internet ingress; access is chained through security-group references instead of broad CIDR rules.
  • Frontend and backend installation, configuration, process supervision, and Nginx routing are automated through EC2 user data.
  • The reusable module boundaries make the scaffold easier to extend without collapsing networking, security, compute, load balancing, and data resources into one file.

Explore another case study