<?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: Kopalachandran Abinash</title>
    <description>The latest articles on DEV Community by Kopalachandran Abinash (@abinash1417).</description>
    <link>https://dev.to/abinash1417</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3967915%2F7c6ac29b-64c0-4522-8bb9-c1ba83f7bad6.png</url>
      <title>DEV Community: Kopalachandran Abinash</title>
      <link>https://dev.to/abinash1417</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abinash1417"/>
    <language>en</language>
    <item>
      <title>How AI Applications Answer From Your Data, Not Their Training</title>
      <dc:creator>Kopalachandran Abinash</dc:creator>
      <pubDate>Sat, 06 Jun 2026 06:26:01 +0000</pubDate>
      <link>https://dev.to/abinash1417/how-i-used-rag-to-make-my-ai-pdf-generator-actually-smart-7h4</link>
      <guid>https://dev.to/abinash1417/how-i-used-rag-to-make-my-ai-pdf-generator-actually-smart-7h4</guid>
      <description>&lt;p&gt;&lt;em&gt;Why retrieval-augmented generation has become the foundational pattern for building useful AI — and how it actually works.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With Relying on LLMs Alone
&lt;/h2&gt;

&lt;p&gt;Large language models are impressive. They can write, reason, summarize, and explain across an enormous range of topics. But they have a hard boundary: their knowledge stops at their training cutoff. Anything that happened after that date, anything specific to your company, your codebase, or your documents — the model simply doesn't know it.&lt;/p&gt;

&lt;p&gt;The naive solution is to paste your data directly into the prompt. For short content, this works. But prompts have limits. A model can only process so much text at once, and even within that limit, quality degrades when you stuff too much context in. The model loses track of things buried in the middle, confuses similar passages, and starts guessing when it should be reading.&lt;/p&gt;

&lt;p&gt;RAG — Retrieval-Augmented Generation — solves this properly. Instead of sending everything to the model and hoping for the best, you send only what's actually relevant to the question being asked.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;p&gt;The analogy that makes RAG click immediately: imagine a student sitting an open-book exam. They don't memorize the entire textbook. When they see a question, they flip to the right chapter, read the relevant section, and write their answer from what they just read. They're not guessing. They're grounding their answer in the source material.&lt;/p&gt;

&lt;p&gt;RAG does exactly this. When a user asks a question, the system finds the most relevant pieces of information from your data, hands those pieces to the LLM as context, and the model answers from that context alone. The result is accurate, grounded, and verifiable — you can point to exactly which source the answer came from.&lt;/p&gt;

&lt;p&gt;The process runs in two phases: ingestion, which prepares your data in advance, and retrieval, which happens at query time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase One: Ingestion
&lt;/h2&gt;

&lt;p&gt;Ingestion is the preparation step. Before any user asks anything, you process your data and store it in a way that makes future retrieval fast and accurate.&lt;/p&gt;

&lt;p&gt;The first step is &lt;strong&gt;chunking&lt;/strong&gt; — splitting your documents into smaller pieces. This seems simple but it's actually the most important decision in the entire pipeline. The quality of your chunking determines the quality of everything that follows.&lt;/p&gt;

&lt;p&gt;The naive approach is splitting by a fixed character count — every 1,000 characters becomes a chunk. The problem is that ideas don't stop at character boundaries. A sentence that starts in one chunk and finishes in the next loses meaning in both halves. A definition that spans two chunks can't be found by searching for either half alone.&lt;/p&gt;

&lt;p&gt;A better approach is to split on natural boundaries — paragraph breaks, section headers, or topic shifts — and add a small overlap between consecutive chunks. The overlap, typically 150–200 characters, means a sentence sitting at a boundary appears in both adjacent chunks. Nothing falls through the cracks.&lt;/p&gt;

&lt;p&gt;Once you have your chunks, each one gets converted into a &lt;strong&gt;vector embedding&lt;/strong&gt;. This is where the real power of RAG lives.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Embeddings
&lt;/h2&gt;

&lt;p&gt;An embedding is a way of representing text as a list of numbers — typically hundreds or thousands of them. These numbers aren't arbitrary. They're produced by a model specifically trained to place semantically similar text close together in this numerical space.&lt;/p&gt;

&lt;p&gt;The practical result is striking. "How do I cancel my subscription?" and "What is the process for terminating my account?" will produce embeddings that are numerically close to each other, despite sharing almost no words. Meanwhile, a chunk about shipping logistics will sit far away from both.&lt;/p&gt;

&lt;p&gt;This property enables &lt;strong&gt;semantic search&lt;/strong&gt; — searching by meaning, not by keyword matching. Traditional search fails when the user's words don't match the document's words. Semantic search doesn't have that problem. It finds what the user means, not just what they typed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Vector Database
&lt;/h2&gt;

&lt;p&gt;Your embeddings need somewhere to live. A vector database stores them and, crucially, supports fast similarity search at scale. Given a query vector, it finds the most similar vectors in the collection — often across millions of entries — in milliseconds.&lt;/p&gt;

&lt;p&gt;Popular options include Pinecone, Weaviate, Qdrant, and pgvector for teams already using PostgreSQL. The right choice depends on scale and infrastructure preferences, but they all support the same core operation: nearest-neighbor search in high-dimensional vector space.&lt;/p&gt;

&lt;p&gt;One detail that matters in real applications: always filter your search by the relevant data source. In a multi-tenant application, you want to search within a specific user's documents, not across every document in the system. Vector databases support metadata filtering alongside similarity search, so you can combine both constraints in a single query.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase Two: Retrieval and Generation
&lt;/h2&gt;

&lt;p&gt;When a user submits a question, the retrieval phase runs in three steps.&lt;/p&gt;

&lt;p&gt;First, the question gets embedded using the same model used during ingestion. This produces a vector that represents the meaning of the question.&lt;/p&gt;

&lt;p&gt;Second, the vector database finds the top matching chunks — typically the five most similar — using cosine similarity between the question vector and the stored chunk vectors.&lt;/p&gt;

&lt;p&gt;Third, those chunks get assembled into a context block and passed to the LLM alongside the original question. The prompt explicitly instructs the model to answer only from the provided context. If the answer isn't there, the model should say so rather than guess.&lt;/p&gt;

&lt;p&gt;That last instruction matters more than it might seem. LLMs, when they don't know something, have a tendency to fill the gap with a confident-sounding fabrication. Grounding them in retrieved context dramatically reduces this. The model has specific, relevant material to work from, and you've told it to stick to it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Similarity Threshold
&lt;/h2&gt;

&lt;p&gt;Not every question will have a good answer in your data, and your system needs to handle that gracefully.&lt;/p&gt;

&lt;p&gt;Vector similarity is scored between 0 and 1, where 1 means identical. Even when there's no relevant content at all, the search still returns results — they just have low scores. If you send those low-quality chunks to the LLM anyway, it will do its best with them and may produce something that sounds plausible but has no real grounding.&lt;/p&gt;

&lt;p&gt;The fix is a minimum similarity threshold. If the best match falls below a certain score — 0.7 is a common starting point — skip the LLM entirely and return an honest "this information isn't in the available data." Users find this far more trustworthy than a confident wrong answer. The system knowing what it doesn't know is a feature, not a limitation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bridging the Vocabulary Gap
&lt;/h2&gt;

&lt;p&gt;One persistent challenge in RAG is the mismatch between how users phrase questions and how the underlying documents are written. A user asking about "getting out of a contract" might be looking for a section titled "Termination of Agreement." The embeddings are related but not identical, and retrieval can miss the right chunk.&lt;/p&gt;

&lt;p&gt;The most effective technique for this is called &lt;strong&gt;HyDE — Hypothetical Document Embeddings&lt;/strong&gt;. Instead of embedding the raw user question, you first ask the LLM to generate a brief, formal answer to the question as it might appear in a document. Then you embed that hypothetical answer.&lt;/p&gt;

&lt;p&gt;Because the hypothetical answer uses vocabulary closer to the source documents, the vector search tends to find the right chunks much more reliably. It's one extra LLM call — fast and cheap — that consistently improves retrieval quality on formal, technical, or legal content.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where RAG Fits in the Broader AI Landscape
&lt;/h2&gt;

&lt;p&gt;RAG has become the default pattern for any AI application that needs to work with private, specialized, or frequently updated information. The reasons are practical.&lt;/p&gt;

&lt;p&gt;Fine-tuning a model on your data sounds appealing, but it's expensive, slow, and the model can still hallucinate. It also doesn't help when your data changes — you'd need to fine-tune again. RAG sidesteps all of this. You update your vector store when data changes, and the model immediately has access to the latest version without retraining.&lt;/p&gt;

&lt;p&gt;The pattern powers a wide range of real applications: customer support bots that answer from internal documentation, legal tools that search across thousands of contracts, research assistants that work across academic papers, and enterprise tools that let employees query company knowledge in plain language.&lt;/p&gt;

&lt;p&gt;The implementation varies — different chunking strategies for different content types, different embedding models for different languages and domains, different vector databases for different scales — but the fundamental loop never changes: embed, store, retrieve, generate.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Thing That Actually Determines Quality
&lt;/h2&gt;

&lt;p&gt;Developers new to RAG tend to focus on which LLM to use — GPT-4 versus Claude versus Llama. In practice, the model choice matters far less than the chunking strategy.&lt;/p&gt;

&lt;p&gt;A powerful model fed irrelevant or broken chunks gives bad answers. A careful chunking strategy with a capable-but-not-state-of-the-art model gives good answers almost every time. The retrieval quality is the ceiling on the generation quality. If the right information doesn't make it into the context window, no amount of model intelligence compensates.&lt;/p&gt;

&lt;p&gt;This is a useful mental shift for anyone building RAG systems: invest time in getting the chunking right, set a sensible similarity threshold, add overlap to handle boundaries, and test retrieval quality directly before worrying about which LLM is on the other end.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;RAG is one of those patterns that looks simple on the surface and reveals depth the more seriously you build with it. If you're working on an AI application that needs to reason over your own data, this is the right foundation to start from.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#ai #llm #rag #machinelearning #webdev #buildinpublic&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
    </item>
    <item>
      <title>How I Deployed a MERN Stack App on AWS from Scratch — Step by Step published</title>
      <dc:creator>Kopalachandran Abinash</dc:creator>
      <pubDate>Thu, 04 Jun 2026 09:01:19 +0000</pubDate>
      <link>https://dev.to/abinash1417/how-i-deployed-a-mern-stack-app-on-aws-from-scratch-step-by-steppublished-4gnd</link>
      <guid>https://dev.to/abinash1417/how-i-deployed-a-mern-stack-app-on-aws-from-scratch-step-by-steppublished-4gnd</guid>
      <description>&lt;p&gt;I recently built a full-stack Hospital Management System and deployed it on AWS with a complete CI/CD pipeline. In this article I'll walk you through exactly how I did it — every command, every step, every mistake I made along the way.&lt;/p&gt;

&lt;p&gt;By the end of this article you'll know how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Containerize a MERN app with Docker&lt;/li&gt;
&lt;li&gt;Push images to AWS ECR&lt;/li&gt;
&lt;li&gt;Deploy backend on EC2&lt;/li&gt;
&lt;li&gt;Host frontend on S3&lt;/li&gt;
&lt;li&gt;Set up an Application Load Balancer&lt;/li&gt;
&lt;li&gt;Configure Auto Scaling&lt;/li&gt;
&lt;li&gt;Automate everything with Jenkins CI/CD&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🏗️ Architecture Overview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub Push → Jenkins CI/CD
                ├── Build Docker Images
                ├── Push to AWS ECR
                ├── Deploy Backend → EC2 (via SSM)
                └── Deploy Frontend → S3

Users → S3 (Frontend) → ALB → EC2 (Backend) → MongoDB Atlas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: React.js + Vite + Tailwind CSS&lt;/li&gt;
&lt;li&gt;Backend: Node.js + Express.js + Socket.IO&lt;/li&gt;
&lt;li&gt;Database: MongoDB Atlas&lt;/li&gt;
&lt;li&gt;Storage: Cloudinary&lt;/li&gt;
&lt;li&gt;AI: Groq API&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📋 Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An AWS account (free tier works fine)&lt;/li&gt;
&lt;li&gt;Docker Desktop installed&lt;/li&gt;
&lt;li&gt;Node.js installed&lt;/li&gt;
&lt;li&gt;Jenkins installed locally&lt;/li&gt;
&lt;li&gt;Your MERN app code on GitHub&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Phase 1 — AWS Account Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create IAM Role for EC2
&lt;/h3&gt;

&lt;p&gt;Your EC2 server needs permissions to talk to other AWS services. Instead of using secret keys on the server, we attach a Role.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to IAM → Roles → Create role&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;AWS service&lt;/strong&gt; → &lt;strong&gt;EC2&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Attach these 4 policies:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AmazonS3FullAccess&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmazonEC2ContainerRegistryFullAccess&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmazonSSMManagedInstanceCore&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CloudWatchAgentServerPolicy&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Name it: &lt;code&gt;EC2-Hospital-Role&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Create IAM User for CLI
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;IAM → Users → Create user → name: &lt;code&gt;hospital-cli-user&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Attach policies:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AmazonEC2ContainerRegistryFullAccess&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmazonS3FullAccess&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmazonSSMFullAccess&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmazonEC2FullAccess&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AWSCloudFormationFullAccess&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Security credentials → Create access key → CLI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Download the CSV&lt;/strong&gt; — you can't see the secret key again!&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Phase 2 — AWS CLI Setup
&lt;/h2&gt;

&lt;p&gt;Install AWS CLI from: &lt;a href="https://awscli.amazonaws.com/AWSCLIV2.msi" rel="noopener noreferrer"&gt;https://awscli.amazonaws.com/AWSCLIV2.msi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then configure it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws configure
&lt;span class="c"&gt;# AWS Access Key ID: your-access-key&lt;/span&gt;
&lt;span class="c"&gt;# AWS Secret Access Key: your-secret-key&lt;/span&gt;
&lt;span class="c"&gt;# Default region name: eu-north-1&lt;/span&gt;
&lt;span class="c"&gt;# Default output format: json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your Account ID. ✅&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 3 — ECR (Elastic Container Registry)
&lt;/h2&gt;

&lt;p&gt;ECR is AWS's private Docker Hub. We create 2 repositories — one for backend, one for frontend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ecr create-repository &lt;span class="nt"&gt;--repository-name&lt;/span&gt; hospital-backend &lt;span class="nt"&gt;--region&lt;/span&gt; eu-north-1
aws ecr create-repository &lt;span class="nt"&gt;--repository-name&lt;/span&gt; hospital-frontend &lt;span class="nt"&gt;--region&lt;/span&gt; eu-north-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Login to ECR from your local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; eu-north-1 | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; YOUR_ACCOUNT_ID.dkr.ecr.eu-north-1.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build and push your Docker images:&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;# Build&lt;/span&gt;
docker-compose build

&lt;span class="c"&gt;# Tag&lt;/span&gt;
docker tag hospital-app-backend:latest YOUR_ACCOUNT_ID.dkr.ecr.eu-north-1.amazonaws.com/hospital-backend:latest
docker tag hospital-app-frontend:latest YOUR_ACCOUNT_ID.dkr.ecr.eu-north-1.amazonaws.com/hospital-frontend:latest

&lt;span class="c"&gt;# Push&lt;/span&gt;
docker push YOUR_ACCOUNT_ID.dkr.ecr.eu-north-1.amazonaws.com/hospital-backend:latest
docker push YOUR_ACCOUNT_ID.dkr.ecr.eu-north-1.amazonaws.com/hospital-frontend:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Phase 4 — EC2 Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Launch EC2 Instance
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;EC2 → Launch instance&lt;/li&gt;
&lt;li&gt;Name: &lt;code&gt;hospital-backend-server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;AMI: &lt;strong&gt;Amazon Linux 2023&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Instance type: &lt;strong&gt;t3.micro&lt;/strong&gt; (free tier)&lt;/li&gt;
&lt;li&gt;Create key pair: &lt;code&gt;hospital-key&lt;/code&gt; (.pem format) — &lt;strong&gt;save this file!&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Security group — open ports: &lt;strong&gt;22, 80, 5000&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;IAM instance profile: &lt;code&gt;EC2-Hospital-Role&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Install Docker on EC2
&lt;/h3&gt;

&lt;p&gt;Connect via EC2 Instance Connect, then run:&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="nb"&gt;sudo &lt;/span&gt;yum update &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker ec2-user
newgrp docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run Backend Container
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Login to ECR from EC2&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; eu-north-1 | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; YOUR_ACCOUNT_ID.dkr.ecr.eu-north-1.amazonaws.com

&lt;span class="c"&gt;# Create .env file&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /home/ec2-user/.env &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
PORT=5000
MONGO_URI=your_mongodb_uri
JWT_SECRET=your_jwt_secret
# ... add all your env vars
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Run container&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; hospital-backend &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 5000:5000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; /home/ec2-user/.env &lt;span class="se"&gt;\&lt;/span&gt;
  YOUR_ACCOUNT_ID.dkr.ecr.eu-north-1.amazonaws.com/hospital-backend:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it's running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker logs hospital-backend
&lt;span class="c"&gt;# Should see: Server running on port 5000 ✅&lt;/span&gt;
&lt;span class="c"&gt;# Should see: MongoDB Connected! ✅&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Phase 5 — S3 Frontend Hosting
&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;# Create bucket&lt;/span&gt;
aws s3 mb s3://your-app-frontend &lt;span class="nt"&gt;--region&lt;/span&gt; eu-north-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In AWS Console:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;S3 → your bucket → Properties → Static website hosting → Enable&lt;/li&gt;
&lt;li&gt;Index document: &lt;code&gt;index.html&lt;/code&gt;, Error document: &lt;code&gt;index.html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Permissions → Block public access → &lt;strong&gt;untick all&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Add bucket policy:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::your-app-frontend/*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build and upload frontend:&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;# Set your backend URL in frontend/.env&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"VITE_API_URL=http://YOUR_ALB_DNS/api"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; frontend/.env

&lt;span class="c"&gt;# Build&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;frontend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build

&lt;span class="c"&gt;# Upload&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;dist/ s3://your-app-frontend &lt;span class="nt"&gt;--region&lt;/span&gt; eu-north-1 &lt;span class="nt"&gt;--delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Phase 6 — Application Load Balancer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;WHY:&lt;/strong&gt; The ALB gives you a stable DNS name that never changes even if EC2 restarts. It also enables Auto Scaling.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;EC2 → Target Groups → Create&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;hospital-tg&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Protocol: HTTP, Port: 5000&lt;/li&gt;
&lt;li&gt;Health check path: &lt;code&gt;/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Register your EC2 instance&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;EC2 → Load Balancers → Create → Application Load Balancer&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;hospital-alb&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Internet-facing, all availability zones&lt;/li&gt;
&lt;li&gt;Listener: HTTP:80 → forward to &lt;code&gt;hospital-tg&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wait for &lt;strong&gt;Active&lt;/strong&gt; status, then copy the DNS name.&lt;/p&gt;

&lt;p&gt;Update your frontend &lt;code&gt;.env&lt;/code&gt; with the ALB DNS and redeploy to S3.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 7 — Auto Scaling
&lt;/h2&gt;

&lt;p&gt;Auto Scaling automatically adds more EC2 instances when CPU is high and removes them when traffic drops.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create AMI from your running EC2 instance&lt;/li&gt;
&lt;li&gt;Create Launch Template using that AMI&lt;/li&gt;
&lt;li&gt;Create Auto Scaling Group:

&lt;ul&gt;
&lt;li&gt;Min: 1, Desired: 1, Max: 3&lt;/li&gt;
&lt;li&gt;CPU tracking policy: 50%&lt;/li&gt;
&lt;li&gt;Attach to your ALB target group&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Phase 8 — Jenkins CI/CD Pipeline
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;WHY:&lt;/strong&gt; Every time you push code, Jenkins automatically builds, pushes, and deploys everything. No manual work needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add credentials in Jenkins:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aws-access-key&lt;/code&gt; (Secret text)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws-secret-key&lt;/code&gt; (Secret text)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Jenkinsfile:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;
    &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aws-access-key'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aws-secret-key'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;AWS_REGION&lt;/span&gt;            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'eu-north-1'&lt;/span&gt;
        &lt;span class="n"&gt;AWS_ACCOUNT_ID&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_ACCOUNT_ID'&lt;/span&gt;
        &lt;span class="n"&gt;ECR_BACKEND&lt;/span&gt;           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_ACCOUNT_ID.dkr.ecr.eu-north-1.amazonaws.com/hospital-backend'&lt;/span&gt;
        &lt;span class="n"&gt;ECR_FRONTEND&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_ACCOUNT_ID.dkr.ecr.eu-north-1.amazonaws.com/hospital-frontend'&lt;/span&gt;
        &lt;span class="n"&gt;S3_BUCKET&lt;/span&gt;             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your-app-frontend'&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Checkout'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="nl"&gt;branch:&lt;/span&gt; &lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;url:&lt;/span&gt; &lt;span class="s1"&gt;'https://github.com/YOUR_USERNAME/YOUR_REPO.git'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Build Images'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="s1"&gt;'docker build -t hospital-backend ./backend'&lt;/span&gt;
                &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="s1"&gt;'docker build -t hospital-frontend ./frontend'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Push to ECR'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="s1"&gt;'"C:\\Program Files\\Amazon\\AWSCLIV2\\aws.exe" ecr get-login-password --region %AWS_REGION% | docker login --username AWS --password-stdin %AWS_ACCOUNT_ID%.dkr.ecr.%AWS_REGION%.amazonaws.com'&lt;/span&gt;
                &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="s1"&gt;'docker tag hospital-backend:latest %ECR_BACKEND%:latest'&lt;/span&gt;
                &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="s1"&gt;'docker tag hospital-frontend:latest %ECR_FRONTEND%:latest'&lt;/span&gt;
                &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="s1"&gt;'docker push %ECR_BACKEND%:latest'&lt;/span&gt;
                &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="s1"&gt;'docker push %ECR_FRONTEND%:latest'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Deploy Backend via SSM'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;instanceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="nl"&gt;returnStdout:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="nl"&gt;script:&lt;/span&gt; &lt;span class="s1"&gt;'"C:\\Program Files\\Amazon\\AWSCLIV2\\aws.exe" ec2 describe-instances --filters "Name=tag:Name,Values=hospital-backend-server" "Name=instance-state-name,Values=running" --query "Reservations[0].Instances[0].InstanceId" --output text --region %AWS_REGION%'&lt;/span&gt;
                    &lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;readLines&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;last&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="s2"&gt;"""
                        "C:\\Program Files\\Amazon\\AWSCLIV2\\aws.exe" ssm send-command ^
                        --instance-ids ${instanceId} ^
                        --document-name "AWS-RunShellScript" ^
                        --parameters "commands=['aws ecr get-login-password --region eu-north-1 | docker login --username AWS --password-stdin %AWS_ACCOUNT_ID%.dkr.ecr.eu-north-1.amazonaws.com &amp;amp;&amp;amp; docker pull %ECR_BACKEND%:latest &amp;amp;&amp;amp; docker stop hospital-backend || true &amp;amp;&amp;amp; docker rm hospital-backend || true &amp;amp;&amp;amp; docker run -d --name hospital-backend --restart always -p 5000:5000 --env-file /home/ec2-user/.env %ECR_BACKEND%:latest']" ^
                        --region %AWS_REGION%
                    """&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Deploy Frontend to S3'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="s1"&gt;'cd frontend &amp;amp;&amp;amp; npm install &amp;amp;&amp;amp; npm run build'&lt;/span&gt;
                &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="s1"&gt;'"C:\\Program Files\\Amazon\\AWSCLIV2\\aws.exe" s3 sync frontend\\dist\\ s3://%S3_BUCKET% --region %AWS_REGION% --delete'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Deployment successful!'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;failure&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Deployment failed!'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key lesson: Keep secrets out of code!
&lt;/h3&gt;

&lt;p&gt;Use Jenkins credentials for all secrets. GitHub will block your push if it detects API keys in your code (learned this the hard way! 😅).&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 9 — CloudFormation (Infrastructure as Code)
&lt;/h2&gt;

&lt;p&gt;Define your infrastructure in a YAML file so you can recreate everything with one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Hospital App Infrastructure&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;BackendRepository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::ECR::Repository&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RepositoryName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hospital-backend&lt;/span&gt;

  &lt;span class="na"&gt;FrontendBucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::S3::Bucket&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-app-frontend&lt;/span&gt;
      &lt;span class="na"&gt;WebsiteConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;IndexDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
        &lt;span class="na"&gt;ErrorDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;

  &lt;span class="na"&gt;HospitalSecurityGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::SecurityGroup&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;GroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hospital-sg&lt;/span&gt;
      &lt;span class="na"&gt;GroupDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Security group for Hospital App&lt;/span&gt;
      &lt;span class="na"&gt;SecurityGroupIngress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
          &lt;span class="na"&gt;FromPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
          &lt;span class="na"&gt;ToPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
          &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
          &lt;span class="na"&gt;FromPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
          &lt;span class="na"&gt;ToPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
          &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation deploy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template-file&lt;/span&gt; cloudformation.yml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stack-name&lt;/span&gt; hospital-stack &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; eu-north-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🐛 Mistakes I Made (So You Don't Have To)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Secrets in Jenkinsfile&lt;/strong&gt;&lt;br&gt;
I accidentally put my Groq API key directly in the Jenkinsfile. GitHub blocked the push immediately. Always use Jenkins credentials instead of hardcoding secrets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Wrong VITE_API_URL&lt;/strong&gt;&lt;br&gt;
After deployment the frontend was hitting &lt;code&gt;localhost:5000&lt;/code&gt; instead of the ALB URL. Always double check your &lt;code&gt;.env&lt;/code&gt; before building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Missing IAM permissions&lt;/strong&gt;&lt;br&gt;
I kept getting &lt;code&gt;UnauthorizedOperation&lt;/code&gt; errors because my IAM user was missing EC2 and CloudFormation permissions. Add all needed permissions upfront.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. SSM parameter parsing&lt;/strong&gt;&lt;br&gt;
Passing environment variables through SSM commands with special characters caused parsing errors. The fix was using &lt;code&gt;--env-file&lt;/code&gt; on EC2 instead of passing each variable individually.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Results
&lt;/h2&gt;

&lt;p&gt;After all this setup, my CI/CD pipeline works like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I push code to GitHub&lt;/li&gt;
&lt;li&gt;Jenkins automatically triggers&lt;/li&gt;
&lt;li&gt;Docker images built and pushed to ECR&lt;/li&gt;
&lt;li&gt;Backend deployed to EC2 via SSM (no SSH needed!)&lt;/li&gt;
&lt;li&gt;Frontend built and uploaded to S3&lt;/li&gt;
&lt;li&gt;Everything live in ~10 minutes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;No manual deployment steps. Ever.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Cost
&lt;/h2&gt;

&lt;p&gt;Running this on AWS free tier costs approximately &lt;strong&gt;$0/month&lt;/strong&gt; if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use t3.micro EC2 (750 hours/month free)&lt;/li&gt;
&lt;li&gt;Stop EC2 when not using&lt;/li&gt;
&lt;li&gt;S3 storage is practically free for a small app&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Repo: &lt;a href="https://github.com/abinash1417/Hospital-app" rel="noopener noreferrer"&gt;https://github.com/abinash1417/Hospital-app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live Demo: &lt;a href="http://hospital-app-frontend-159372.s3-website.eu-north-1.amazonaws.com" rel="noopener noreferrer"&gt;http://hospital-app-frontend-159372.s3-website.eu-north-1.amazonaws.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS Free Tier: &lt;a href="https://aws.amazon.com/free" rel="noopener noreferrer"&gt;https://aws.amazon.com/free&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Deploying a full-stack app on AWS from scratch taught me more about DevOps than any tutorial. The key things I learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always use IAM roles instead of access keys on servers&lt;/li&gt;
&lt;li&gt;Keep secrets in credential managers, never in code&lt;/li&gt;
&lt;li&gt;Load Balancers are essential for stable URLs&lt;/li&gt;
&lt;li&gt;CI/CD pipelines save enormous amounts of time&lt;/li&gt;
&lt;li&gt;Infrastructure as Code means you never lose your setup again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have any questions, drop them in the comments! Happy to help. 🚀&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If this helped you, please leave a ❤️ and follow for more DevOps content!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cicd</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
