<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Deep Datta</title>
    <description>The latest articles on DEV Community by Deep Datta (@deep_datta_731d9bd4a91980).</description>
    <link>https://dev.to/deep_datta_731d9bd4a91980</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4005240%2F4e5cbaf4-86cb-419d-8034-4bb2a33effb6.jpg</url>
      <title>DEV Community: Deep Datta</title>
      <link>https://dev.to/deep_datta_731d9bd4a91980</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deep_datta_731d9bd4a91980"/>
    <language>en</language>
    <item>
      <title>Deploying Nginx on AWS EC2 Using Terraform — Automating Infrastructure</title>
      <dc:creator>Deep Datta</dc:creator>
      <pubDate>Thu, 09 Jul 2026 07:40:21 +0000</pubDate>
      <link>https://dev.to/deep_datta_731d9bd4a91980/deploying-nginx-on-aws-ec2-using-terraform-automating-infrastructure-2fkp</link>
      <guid>https://dev.to/deep_datta_731d9bd4a91980/deploying-nginx-on-aws-ec2-using-terraform-automating-infrastructure-2fkp</guid>
      <description>&lt;p&gt;Been meaning to do a proper Terraform project for a while. This weekend I finally did it.&lt;/p&gt;

&lt;p&gt;The goal was simple: use Terraform to provision AWS infrastructure from scratch and have a running Nginx server at the end of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Here's the architecture at a glance:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgehz7tcmgchrd7op6txe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgehz7tcmgchrd7op6txe.png" alt=" " width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A browser hits an Internet Gateway, which routes traffic into a VPC containing a public subnet. Inside that subnet lives an EC2 instance running Nginx in a Docker container, exposed on port 8080.&lt;/p&gt;


&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── main.tf
├── terraform.tfvars
└── entrypoint.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1: Variables
&lt;/h2&gt;

&lt;p&gt;Instead of hardcoding values, I declared variables and supplied them via &lt;code&gt;terraform.tfvars&lt;/code&gt;. This keeps things reusable and environment-aware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;terraform.tfvars&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;vpc_cidr_block&lt;/span&gt;      &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
&lt;span class="nx"&gt;subnet_1_cidr_block&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.10.0/24"&lt;/span&gt;
&lt;span class="nx"&gt;env_prefix&lt;/span&gt;          &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;
&lt;span class="nx"&gt;avail_zone&lt;/span&gt;          &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-south-1a"&lt;/span&gt;
&lt;span class="nx"&gt;my_ip&lt;/span&gt;               &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"YOUR_IP/32"&lt;/span&gt;   &lt;span class="c1"&gt;# replaced with my actual IP&lt;/span&gt;
&lt;span class="nx"&gt;instance_type&lt;/span&gt;       &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;⚠️ Never commit your real IP or credentials to a public repo. Add &lt;code&gt;terraform.tfvars&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Step 2: Provider
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-south-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This tells Terraform to use AWS and deploy everything into the Mumbai region (&lt;code&gt;ap-south-1&lt;/code&gt;).&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 3: Networking
&lt;/h2&gt;
&lt;h3&gt;
  
  
  VPC
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"myapp-vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr_block&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.env_prefix}-vpc"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This creates an isolated virtual network with the CIDR &lt;code&gt;10.0.0.0/16&lt;/code&gt;, providing me 65,536 private IP addresses to work with.&lt;/p&gt;
&lt;h3&gt;
  
  
  Subnet
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"myapp-subnet-1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myapp-vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet_1_cidr_block&lt;/span&gt;
  &lt;span class="nx"&gt;availability_zone&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avail_zone&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.env_prefix}-subnet-1"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This carves out a &lt;code&gt;/24&lt;/code&gt; subnet (&lt;code&gt;10.0.10.0/24&lt;/code&gt;) — 256 addresses — inside the VPC, pinned to &lt;code&gt;ap-south-1a&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Internet Gateway
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_internet_gateway"&lt;/span&gt; &lt;span class="s2"&gt;"myapp-igw"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myapp-vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.env_prefix}-internet-gateway"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By default, a VPC is completely isolated. The Internet Gateway is what allows it to communicate with the outside world.&lt;/p&gt;
&lt;h3&gt;
  
  
  Route Table
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_default_route_table"&lt;/span&gt; &lt;span class="s2"&gt;"main-rtb"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default_route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myapp-vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default_route_table_id&lt;/span&gt;
  &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;
    &lt;span class="nx"&gt;gateway_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myapp-igw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.env_prefix}-main-rtb"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Rather than creating a new route table and manually associating it with the subnet, I modified the &lt;strong&gt;default route table&lt;/strong&gt; that AWS automatically creates with the VPC. The &lt;code&gt;0.0.0.0/0&lt;/code&gt; route sends all outbound traffic through the Internet Gateway.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I initially wrote a custom &lt;code&gt;aws_route_table&lt;/code&gt; resource, but switched to &lt;code&gt;aws_default_route_table&lt;/code&gt; to keep things simpler — it's automatically associated with all subnets that don't have an explicit association.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Step 4: Security Group
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_default_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"default-sg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myapp-vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my_ip&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# SSH only from your machine&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# HTTP open to the world&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;egress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"-1"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;prefix_list_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.env_prefix}-default-sg"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Two ingress rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Port 22 (SSH)&lt;/strong&gt; — locked down to your IP only. No random internet scanners getting in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port 80 (HTTP)&lt;/strong&gt; — open to everyone so the web server is publicly accessible.
All outbound traffic is allowed (&lt;code&gt;protocol = "-1"&lt;/code&gt; means all protocols).&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Step 5: Fetching the Latest Ubuntu AMI Dynamically
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_ami"&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;most_recent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;owners&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"amazon"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ubuntu/images/hvm-ssd/ubuntu-*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"virtualization-type"&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hvm"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"ami_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ubuntu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Instead of hardcoding an AMI ID (which is region-specific and goes stale), I used a &lt;strong&gt;data source&lt;/strong&gt; to dynamically fetch the most recent Ubuntu HVM AMI. The &lt;code&gt;output&lt;/code&gt; block prints the resolved AMI ID after &lt;code&gt;terraform apply&lt;/code&gt;, which is handy for debugging.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 6: EC2 Instance
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"myapp-instance"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ubuntu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myapp-subnet-1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_default_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default-sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;availability_zone&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avail_zone&lt;/span&gt;

  &lt;span class="nx"&gt;user_data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"entrypoint.sh"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.env_prefix}-server"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The instance is a &lt;code&gt;t2.micro&lt;/code&gt; (free-tier eligible), placed in my subnet with the security group I defined. The &lt;code&gt;user_data&lt;/code&gt; field passes &lt;code&gt;entrypoint.sh&lt;/code&gt; as a bootstrap script that runs once on first boot.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 7: The Bootstrap Script
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;entrypoint.sh&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;docker.io &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker ubuntu
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This script:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Updates the package index&lt;/li&gt;
&lt;li&gt;Installs Docker&lt;/li&gt;
&lt;li&gt;Starts the Docker daemon&lt;/li&gt;
&lt;li&gt;Adds the &lt;code&gt;ubuntu&lt;/code&gt; user to the &lt;code&gt;docker&lt;/code&gt; group&lt;/li&gt;
&lt;li&gt;Pulls and runs the official &lt;strong&gt;Nginx&lt;/strong&gt; image, mapping host port &lt;code&gt;8080&lt;/code&gt; → container port &lt;code&gt;80&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Deploying
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Initialize — downloads the AWS provider plugin&lt;/span&gt;
terraform init

&lt;span class="c"&gt;# Preview what will be created&lt;/span&gt;
terraform plan

&lt;span class="c"&gt;# Apply — this actually creates the resources&lt;/span&gt;
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then visit &lt;code&gt;http://&amp;lt;public-ip&amp;gt;:8080&lt;/code&gt; in your browser — you should see the Nginx welcome page.&lt;/p&gt;



&lt;p&gt;All the code for this project is available on github &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dxdprojects" rel="noopener noreferrer"&gt;
        dxdprojects
      &lt;/a&gt; / &lt;a href="https://github.com/dxdprojects/aws-ec2-nginx-terraform" rel="noopener noreferrer"&gt;
        aws-ec2-nginx-terraform
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>terraform</category>
      <category>devops</category>
      <category>aws</category>
    </item>
    <item>
      <title>Building a Production-Ready 3-Tier Application with Kubernetes Blue-Green Deployment</title>
      <dc:creator>Deep Datta</dc:creator>
      <pubDate>Tue, 30 Jun 2026 12:23:02 +0000</pubDate>
      <link>https://dev.to/deep_datta_731d9bd4a91980/building-a-production-ready-3-tier-application-with-kubernetes-blue-green-deployment-5b1g</link>
      <guid>https://dev.to/deep_datta_731d9bd4a91980/building-a-production-ready-3-tier-application-with-kubernetes-blue-green-deployment-5b1g</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I took a basic 3-tier Node.js and MySQL application and transformed it into a production-ready system using DevOps practices. The goal was to implement a zero-downtime deployment strategy using Kubernetes' blue-green deployment pattern, containerize the application, automate the CI/CD pipeline with Jenkins, and provision infrastructure with Terraform.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding the Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The 3-Tier Application
&lt;/h3&gt;

&lt;p&gt;The application consists of three layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Frontend (React)&lt;/strong&gt;: A user management interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend (Node.js/Express)&lt;/strong&gt;: An API server handling CRUD operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database (MySQL)&lt;/strong&gt;: Persistent data storage.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 1: Containerizing with Docker
&lt;/h2&gt;

&lt;p&gt;I used a multi-stage Dockerfile to keep the final image lightweight and efficient.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:14-alpine

WORKDIR /usr/src/app/client
COPY client/package*.json ./
RUN npm install
COPY client/ ./
RUN npm run build

WORKDIR /usr/src/app/server
COPY server/package*.json ./
RUN npm install
COPY server/ ./
RUN mkdir -p ./public &amp;amp;&amp;amp; cp -R /usr/src/app/client/dist/* ./public/

EXPOSE 5000
CMD ["npm", "start"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Orchestrating with Kubernetes
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Key Resources
&lt;/h3&gt;

&lt;p&gt;I created two identical deployments—blue and green. The selector field allows us to switch traffic.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Service
metadata:
  name: app
spec:
  ports:
  - port: 80
    targetPort: 5000
  selector:
    app: app
    version: blue  # Points to the active environment
  type: LoadBalancer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Blue-Green Deployment Strategy
&lt;/h2&gt;

&lt;p&gt;The Jenkinsfile contains the logic for switching traffic by patching the service:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stage('Switch Traffic') {
    when { expression { return params.SWITCH_TRAFFIC } }
    steps {
        script {
            def newEnv = params.DEPLOY_ENV
            withKubeConfig(...) {
                sh "kubectl patch service app -p '{\"spec\":{\"selector\":{\"version\":\"${newEnv}\"}}}' --type merge -n ${KUBE_NAMESPACE}"
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting: Real Issues I Encountered
&lt;/h2&gt;

&lt;p&gt;To keep this guide readable, I've categorized the hurdles I faced. Click to expand each issue.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  Issue 1: Docker Build Failures (Node Cache)
  &lt;br&gt;
Problem: The build stage would fail intermittently with npm dependency resolution errors.&lt;br&gt;
Solution: Added explicit cache busting using --no-cache and a .dockerignore file.&lt;br&gt;


&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  Issue 2: Application Pod Can't Connect to MySQL
  &lt;br&gt;
Problem: App pod crashed because MySQL wasn't ready.&lt;br&gt;
Solution: Added a retry loop in the Node.js connection logic and used a Kubernetes Headless Service for stable DNS.&lt;br&gt;


&lt;p&gt;&lt;/p&gt;



&lt;p&gt;Here are some screenshots from the Jenkins build pipeline&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzvb59whna53mzk0lzs9j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzvb59whna53mzk0lzs9j.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
The application is deployed in the blue environment (above image)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkpvzuz18arm3xymvx6kh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkpvzuz18arm3xymvx6kh.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff43d6y64s0ao37mvwocp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff43d6y64s0ao37mvwocp.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
The application is now deployed in the green environment (above 2 images)&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dxdprojects" rel="noopener noreferrer"&gt;
        dxdprojects
      &lt;/a&gt; / &lt;a href="https://github.com/dxdprojects/3-Tier-NodeJS-MySql" rel="noopener noreferrer"&gt;
        3-Tier-NodeJS-MySql
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;User Management App&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;This is a full-stack application for managing users with a front-end built using HTML, CSS, and JavaScript, and a back-end powered by Node.js, Express, and MySQL.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of Contents&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dxdprojects/3-Tier-NodeJS-MySql#features" rel="noopener noreferrer"&gt;Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dxdprojects/3-Tier-NodeJS-MySql#prerequisites" rel="noopener noreferrer"&gt;Prerequisites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/dxdprojects/3-Tier-NodeJS-MySql#setup-instructions" rel="noopener noreferrer"&gt;Setup Instructions&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dxdprojects/3-Tier-NodeJS-MySql#1-setting-up-mysql-server" rel="noopener noreferrer"&gt;1. Setting Up MySQL Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dxdprojects/3-Tier-NodeJS-MySql#2-configuring-and-running-the-client" rel="noopener noreferrer"&gt;2. Configuring and Running the Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dxdprojects/3-Tier-NodeJS-MySql#3-configuring-and-running-the-server" rel="noopener noreferrer"&gt;3. Configuring and Running the Server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/dxdprojects/3-Tier-NodeJS-MySql#usage" rel="noopener noreferrer"&gt;Usage&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/dxdprojects/3-Tier-NodeJS-MySql#license" rel="noopener noreferrer"&gt;License&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Add new users with a name, email, and role (User/Admin).&lt;/li&gt;
&lt;li&gt;View a list of all users.&lt;/li&gt;
&lt;li&gt;Edit user details.&lt;/li&gt;
&lt;li&gt;Delete users.&lt;/li&gt;
&lt;li&gt;Responsive and user-friendly UI.&lt;/li&gt;
&lt;li&gt;Smooth animations and minimalistic design.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Prerequisites&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Before setting up this project, ensure you have the following installed on your machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/" rel="nofollow noopener noreferrer"&gt;Node.js&lt;/a&gt; (version 12.x or higher)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.mysql.com/" rel="nofollow noopener noreferrer"&gt;MySQL&lt;/a&gt; (version 5.7 or higher)&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setup Instructions&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;1. Setting Up MySQL Server&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;First, you need to set up a MySQL server on your local machine.&lt;/p&gt;


&lt;ol&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Update the package index:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;sudo apt update&lt;/pre&gt;

&lt;/div&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Install the MySQL server:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;sudo apt install mysql-server&lt;/pre&gt;

&lt;/div&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Log in to the&lt;/strong&gt;…&lt;/p&gt;


&lt;/li&gt;

&lt;/ol&gt;&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dxdprojects/3-Tier-NodeJS-MySql" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>jenkins</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Taking an Existing App and Deploying It with Docker, Kubernetes, and Terraform</title>
      <dc:creator>Deep Datta</dc:creator>
      <pubDate>Mon, 29 Jun 2026 12:06:50 +0000</pubDate>
      <link>https://dev.to/deep_datta_731d9bd4a91980/taking-an-existing-app-and-deploying-it-with-docker-kubernetes-and-terraform-4ol5</link>
      <guid>https://dev.to/deep_datta_731d9bd4a91980/taking-an-existing-app-and-deploying-it-with-docker-kubernetes-and-terraform-4ol5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpsgrffuw9q1bifq68zgo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpsgrffuw9q1bifq68zgo.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I found a simple banking application on GitHub and used it as the base for my learning project. I did not build the application itself, but I did take it through the &lt;strong&gt;DevOps journey&lt;/strong&gt; — containerizing it, deploying it on Kubernetes, and setting up infrastructure on AWS with Terraform.&lt;/p&gt;

&lt;p&gt;I wanted to improve my understanding of how an application moves from a local project to a real environment where it can actually run and be used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Chose This Project
&lt;/h2&gt;

&lt;p&gt;I wanted a project that would help me understand DevOps in a practical way. A banking app was a good choice because it already had a clear purpose and some important features like login, dashboard, deposits, withdrawals, and transactions.&lt;/p&gt;

&lt;p&gt;The interesting part for me was not just the app itself. It was the journey of taking that app and making it ready for deployment. Instead of building a small demo app from zero, I focused on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Packaging&lt;/strong&gt; the app with Docker&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploying&lt;/strong&gt; it with Kubernetes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managing&lt;/strong&gt; the database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provisioning&lt;/strong&gt; infrastructure using Terraform&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The First Step: Understanding the Application
&lt;/h2&gt;

&lt;p&gt;Before doing anything with Docker or Kubernetes, I took time to understand the project structure. The app was built using &lt;strong&gt;Java and Spring Boot&lt;/strong&gt;, and it used &lt;strong&gt;MySQL&lt;/strong&gt; for data storage. &lt;/p&gt;

&lt;p&gt;That was already enough for me to understand that this project could become a good example of a multi-tier application. Since the original README wasn't very detailed, I cloned the repository and created a detailed README from scratch to document my process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dockerizing the Application
&lt;/h2&gt;

&lt;p&gt;Once I understood the app, the next step was to containerize it. I wrote a Dockerfile to package the environment and the application, created a Docker image, and pushed it to my Docker Hub registry. This made the application portable and ready for any environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving to Kubernetes
&lt;/h2&gt;

&lt;p&gt;After Docker, the next step was Kubernetes. Honestly, this part felt a little intimidating at first.&lt;/p&gt;

&lt;p&gt;I used Kubernetes to deploy both the application and the MySQL database. This was a very important experience because it showed me that deployment is not just about “running an app.” It is also about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensuring the app can connect to the database.&lt;/li&gt;
&lt;li&gt;Persisting data so it isn't lost when a container restarts.&lt;/li&gt;
&lt;li&gt;Keeping the service available to users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I learned that databases need more careful planning than stateless applications. You need to think about storage, persistence, and configuration. &lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform and AWS: The Infrastructure Part
&lt;/h2&gt;

&lt;p&gt;After Docker and Kubernetes, I wanted to learn how infrastructure could be created using code (IaC). That is where Terraform came in.&lt;/p&gt;

&lt;p&gt;I used Terraform to provision the AWS resources for the project, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VPC &amp;amp; Subnets&lt;/strong&gt;: For networking logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internet Gateway&lt;/strong&gt;: To allow traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EKS Cluster&lt;/strong&gt;: To run our Kubernetes nodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Roles &amp;amp; Security Groups&lt;/strong&gt;: To manage permissions and security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This part taught me that DevOps is just as much about the environment as it is about the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned From This Project
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. You do not need to start from zero
&lt;/h3&gt;

&lt;p&gt;One of the best things about this project was that I did not need to build the whole application myself. By using an existing app, I could focus 100% on the deployment and infrastructure side.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Troubleshooting is normal
&lt;/h3&gt;

&lt;p&gt;There were many times when things did not work—database connection errors or service exposure issues. These frustrations were actually where the most learning happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Security matters
&lt;/h3&gt;

&lt;p&gt;I gained a basic understanding of why secrets, permissions, and access control are vital in real-world cloud environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;If you are a beginner in DevOps, I think this kind of project is a great place to start. You do not need to build everything yourself. You can take an existing project and focus on improving the deployment and infrastructure side of it.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dxdprojects" rel="noopener noreferrer"&gt;
        dxdprojects
      &lt;/a&gt; / &lt;a href="https://github.com/dxdprojects/Multi-Tier-Bank-Application-on-Kubernetes" rel="noopener noreferrer"&gt;
        Multi-Tier-Bank-Application-on-Kubernetes
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Multi-Tier Bank Application on Kubernetes&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A full-stack banking application deployed using modern DevOps practices. This project demonstrates how to build a Spring Boot web app, containerize it with Docker, deploy it on Kubernetes, and provision an AWS EKS environment with Terraform.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Project Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This repository contains a simple multi-tier banking web application with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a Spring Boot backend&lt;/li&gt;
&lt;li&gt;a MySQL database&lt;/li&gt;
&lt;li&gt;Docker-based containerization&lt;/li&gt;
&lt;li&gt;Kubernetes manifests for deployment&lt;/li&gt;
&lt;li&gt;Terraform infrastructure as code for AWS EKS&lt;/li&gt;
&lt;li&gt;RBAC setup guidance for CI/CD automation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal of this project is to showcase a practical end-to-end DevOps workflow from application development to cloud deployment.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What the Application Does&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Users can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;register an account&lt;/li&gt;
&lt;li&gt;log in securely&lt;/li&gt;
&lt;li&gt;view their dashboard&lt;/li&gt;
&lt;li&gt;deposit funds&lt;/li&gt;
&lt;li&gt;withdraw funds&lt;/li&gt;
&lt;li&gt;transfer money to another user&lt;/li&gt;
&lt;li&gt;view transaction history&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The application is built with Spring Boot, Spring Security, Thymeleaf, and MySQL.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Architecture&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;The project follows a multi-tier architecture:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Frontend: Thymeleaf-based web UI&lt;/li&gt;
&lt;li&gt;Backend: Spring…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dxdprojects/Multi-Tier-Bank-Application-on-Kubernetes" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Building DevOps Projects from scratch to enterprise-grade</title>
      <dc:creator>Deep Datta</dc:creator>
      <pubDate>Sat, 27 Jun 2026 11:39:25 +0000</pubDate>
      <link>https://dev.to/deep_datta_731d9bd4a91980/building-devops-projects-from-scratch-to-enterprise-grade-1ii6</link>
      <guid>https://dev.to/deep_datta_731d9bd4a91980/building-devops-projects-from-scratch-to-enterprise-grade-1ii6</guid>
      <description>&lt;p&gt;Hey everyone,&lt;/p&gt;

&lt;p&gt;I’ve decided to push myself out of my comfort zone. For the next 45 days, I am committing to building and documenting DevOps projects every single day—ranging from small-scale to enterprise-grade architectures.&lt;/p&gt;

&lt;p&gt;Every single day, I’ll be deploying a project and writing a detailed breakdown right here on DEV.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Framework
&lt;/h2&gt;

&lt;p&gt;I won't just be dropping lines of code or a random GitHub link. For every single project, I’m going to deep-dive into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The What:&lt;/strong&gt; What exactly am I building?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Why:&lt;/strong&gt; Why does this architecture matter? What real-world problem does it solve?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Tech Stack:&lt;/strong&gt; Exactly why I chose specific tools instead of just picking things at random.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What to Expect
&lt;/h2&gt;

&lt;p&gt;I’m starting small to lock in the fundamentals, but the complexity is going to ramp up fast. You can expect to see projects covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local development environments and containerization&lt;/li&gt;
&lt;li&gt;CI/CD pipelines that actually mimic production workflows&lt;/li&gt;
&lt;li&gt;Infrastructure as Code (IaC) with modular, reusable setups&lt;/li&gt;
&lt;li&gt;Observability, monitoring, and alerting dashboards&lt;/li&gt;
&lt;li&gt;Cloud-native Kubernetes deployments and GitOps workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you are also on your DevOps journey, a seasoned engineer looking to critique my architecture, or a hiring manager looking for someone who loves to build and document out in the open—I’d love to have you follow along.&lt;/p&gt;

&lt;p&gt;Day 1 drops tomorrow. Let’s build.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://github.com/dxdprojects" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F171801242%3Fv%3D4%3Fs%3D400" height="425" class="m-0" width="425"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://github.com/dxdprojects" rel="noopener noreferrer" class="c-link"&gt;
            dxdprojects (Deep Datta) · GitHub
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            dxdprojects has 5 repositories available. Follow their code on GitHub.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.githubassets.com%2Ffavicons%2Ffavicon.svg" width="32" height="32"&gt;
          github.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>devops</category>
    </item>
  </channel>
</rss>
