<?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: Uri Gakuru Karanja</title>
    <description>The latest articles on DEV Community by Uri Gakuru Karanja (@gakuruuri).</description>
    <link>https://dev.to/gakuruuri</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%2F1384744%2F3829a719-6eef-40f6-a50e-3bd527a62739.png</url>
      <title>DEV Community: Uri Gakuru Karanja</title>
      <link>https://dev.to/gakuruuri</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gakuruuri"/>
    <language>en</language>
    <item>
      <title>Building a Recipe-Sharing Application</title>
      <dc:creator>Uri Gakuru Karanja</dc:creator>
      <pubDate>Sun, 27 Apr 2025 07:56:44 +0000</pubDate>
      <link>https://dev.to/gakuruuri/building-a-recipe-sharing-application-3gc5</link>
      <guid>https://dev.to/gakuruuri/building-a-recipe-sharing-application-3gc5</guid>
      <description>&lt;h1&gt;
  
  
  Taming the Traffic Spikes: How I Built a Recipe Sharing Platform That Scales Automatically
&lt;/h1&gt;

&lt;p&gt;Picture this: you've built a beautiful recipe website that gets modest traffic most of the day. But when 5 PM rolls around, suddenly 20,000 concurrent users are desperately searching for dinner inspiration. Will your infrastructure buckle under the pressure?&lt;/p&gt;

&lt;p&gt;This was exactly the challenge I faced when building a recipe sharing application on AWS. Today, I'm going to walk you through how I tackled the specific technical challenge of handling unpredictable, spiky traffic patterns while keeping costs under control.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Unpredictable Traffic Patterns
&lt;/h2&gt;

&lt;p&gt;Recipe websites have a peculiar traffic pattern – relatively quiet most of the day, then massive spikes around meal planning times. Our requirements specifically called for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Supporting up to 20,000 concurrent users during peak hours&lt;/li&gt;
&lt;li&gt;Maintaining performance during these spikes&lt;/li&gt;
&lt;li&gt;Keeping costs down during low-traffic periods&lt;/li&gt;
&lt;li&gt;Global distribution for users across different time zones&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Traditional "fixed capacity" architectures would either be overprovisioned (wasting money) or underprovisioned (crashing during peaks).&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.amazonaws.com%2Fuploads%2Farticles%2Fc8ueqpyiglf7068k5p5n.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.amazonaws.com%2Fuploads%2Farticles%2Fc8ueqpyiglf7068k5p5n.png" alt="Image description" width="744" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     Main layers showing presentation, compute, and data layers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  First Attempt: Single EC2 Instance Architecture
&lt;/h2&gt;

&lt;p&gt;My initial approach was straightforward: host everything on a single EC2 instance running both the frontend and backend.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Example FastAPI endpoint in our initial architecture
&lt;/span&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/recipes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_recipes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Fetch directly from database
&lt;/span&gt;    &lt;span class="n"&gt;recipes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM recipes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recipes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;recipes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup worked fine during development but had clear limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single point of failure&lt;/li&gt;
&lt;li&gt;Fixed capacity regardless of actual demand&lt;/li&gt;
&lt;li&gt;No geographic distribution for global users&lt;/li&gt;
&lt;li&gt;Limited scaling options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When load testing with simulated traffic spikes, the instance CPU would max out, and response times would increase dramatically. Not acceptable!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Breakthrough: Decoupled Architecture with Auto-scaling
&lt;/h2&gt;

&lt;p&gt;The solution came from rethinking the entire architecture. Instead of a monolithic design, I decoupled the components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Static Frontend Separated from Dynamic Backend&lt;/strong&gt;: The React.js frontend is now hosted on S3 and distributed through CloudFront&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend API Behind a Load Balancer&lt;/strong&gt;: The FastAPI application runs on EC2 instances in private subnets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NoSQL Database for Scalable Data Access&lt;/strong&gt;: DynamoDB provides consistent performance regardless of scale&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a simplified diagram of the architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  ┌─────────────┐
                  │   Users     │
                  └──────┬──────┘
                         │
           ┌─────────────┴─────────────┐
           ▼                           ▼
  ┌─────────────────┐         ┌─────────────────┐
  │   CloudFront    │         │       ALB       │
  │  (Frontend CDN) │         │ (Load Balancer) │
  └────────┬────────┘         └────────┬────────┘
           │                           │
  ┌────────┴────────┐         ┌────────┴────────┐
  │    S3 Bucket    │         │  Auto Scaling   │
  │    (Frontend)   │         │     Group       │
  └─────────────────┘         └────────┬────────┘
                                       │
                              ┌────────┴────────┐
                              │   EC2 Instance  │
                              │    (Backend)    │
                              └────────┬────────┘
                                       │
                              ┌────────┴────────┐
                              │    DynamoDB     │
                              │   (Database)    │  
                              └─────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.amazonaws.com%2Fuploads%2Farticles%2Fk4pt9uc7foifhgh6dy2t.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.amazonaws.com%2Fuploads%2Farticles%2Fk4pt9uc7foifhgh6dy2t.png" alt="Image description" width="800" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   AWS Architecture Diagram for Recipe Sharing Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  The Implementation: Infrastructure as Code
&lt;/h2&gt;

&lt;p&gt;Rather than manual configuration, I used CloudFormation templates to define the entire infrastructure:&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="c1"&gt;# Snippet from CloudFormation template showing the EC2 Auto Scaling Group&lt;/span&gt;
&lt;span class="na"&gt;RecipeApiAutoScalingGroup&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::AutoScaling::AutoScalingGroup&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;VPCZoneIdentifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;PrivateSubnets&lt;/span&gt;
    &lt;span class="na"&gt;LaunchConfigurationName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;LaunchConfig&lt;/span&gt;
    &lt;span class="na"&gt;MinSize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;
    &lt;span class="na"&gt;MaxSize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;4'&lt;/span&gt;
    &lt;span class="na"&gt;DesiredCapacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2'&lt;/span&gt;
    &lt;span class="na"&gt;TargetGroupARNs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TargetGroup&lt;/span&gt;
    &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
        &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${AWS::StackName}-api-instance&lt;/span&gt;
        &lt;span class="na"&gt;PropagateAtLaunch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.amazonaws.com%2Fuploads%2Farticles%2F6r590unfn2gqviygwum3.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.amazonaws.com%2Fuploads%2Farticles%2F6r590unfn2gqviygwum3.png" alt="Image description" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          CLOUDFORMATION STACK CREATION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This template creates an Auto Scaling Group that can adjust between 1 and 4 instances based on actual demand. The connection between the backend API and DynamoDB was implemented with AWS SDK for Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Example API code accessing DynamoDB
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;dynamodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;recipes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/recipes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_recipes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recipes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Items&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real Examples of Implementation Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Database Choice: SQL vs NoSQL
&lt;/h3&gt;

&lt;p&gt;When designing the data layer, I had to choose between traditional relational databases and NoSQL solutions. The decision came down to our data access patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No complex joins or relationships between entities&lt;/li&gt;
&lt;li&gt;Read-heavy workload (users view recipes far more than they create)&lt;/li&gt;
&lt;li&gt;Need for automatic scaling with no manual intervention&lt;/li&gt;
&lt;li&gt;Simple document structure for recipes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DynamoDB's document model was perfect for storing recipe objects as simple JSON documents:&lt;br&gt;
&lt;/p&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;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"550e8400-e29b-41d4-a716-446655440000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Chocolate Chip Cookies"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Ingredients"&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="s2"&gt;"2 cups all-purpose flour"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="s2"&gt;"1/2 teaspoon baking soda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"1 cup unsalted butter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"1 cup packed brown sugar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"1/2 cup white sugar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"2 eggs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"2 teaspoons vanilla extract"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="s2"&gt;"2 cups semisweet chocolate chips"&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;"Steps"&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="s2"&gt;"Preheat oven to 350°F (175°C)."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Cream together butter and sugars until smooth."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Beat in eggs one at a time, then stir in vanilla."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Dissolve baking soda in hot water, add to batter."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Mix in flour, chocolate chips, and nuts."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Drop by large spoonfuls onto ungreased pans."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Bake for about 10 minutes, until edges are browned."&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;&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.amazonaws.com%2Fuploads%2Farticles%2Fkzdbzfshyzrzspbxrn5s.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.amazonaws.com%2Fuploads%2Farticles%2Fkzdbzfshyzrzspbxrn5s.png" alt="Image description" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          DYNAMODB TABLE EXPLORATION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  2. Frontend Content Delivery: S3 + CloudFront
&lt;/h3&gt;

&lt;p&gt;For the frontend, I needed a solution that would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scale globally&lt;/li&gt;
&lt;li&gt;Require zero maintenance&lt;/li&gt;
&lt;li&gt;Provide fast load times worldwide&lt;/li&gt;
&lt;li&gt;Support HTTPS securely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The S3 + CloudFront combination perfectly solved this requirement, automatically distributing content to edge locations closest to users:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example React component configuration pointing to our API endpoint&lt;/span&gt;
&lt;span class="c1"&gt;// in src/config.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;CONFIG_MAX_INGREDIENTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;CONFIG_MAX_STEPS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;CONFIG_MAX_RECIPES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;CONFIG_USER_PAGE_TITLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Discover Amazing Recipes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;CONFIG_ADMIN_PAGE_TITLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipe Management&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.amazonaws.com%2Fuploads%2Farticles%2F9woh8ticlbcn70c5y08n.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.amazonaws.com%2Fuploads%2Farticles%2F9woh8ticlbcn70c5y08n.png" alt="Image description" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              CLOUDFRONT DISTRIBUTION URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  The Results: Performance Under Pressure
&lt;/h2&gt;

&lt;p&gt;After implementation, the architecture handled simulated traffic spikes gracefully:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;During low-traffic periods, a single instance serves all requests (minimizing costs)&lt;/li&gt;
&lt;li&gt;As traffic increases, Auto Scaling adds instances based on CPU metrics&lt;/li&gt;
&lt;li&gt;Read operations on DynamoDB remain consistent even with thousands of concurrent users&lt;/li&gt;
&lt;li&gt;Global users experience fast loading times thanks to CloudFront's global edge locations&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Fd3r6mi1rk60ajy9llkn3.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.amazonaws.com%2Fuploads%2Farticles%2Fd3r6mi1rk60ajy9llkn3.png" alt="Image description" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;         RECIPE SHARING APPLICATION ADMIN PAGE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Questions for Discussion
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Have you experienced similar traffic spike challenges? How did you address them?&lt;/li&gt;
&lt;li&gt;What's your preferred approach to auto-scaling – CPU-based or traffic-based metrics?&lt;/li&gt;
&lt;li&gt;Do you use Infrastructure as Code in your projects, or do you prefer manual configuration?&lt;/li&gt;
&lt;li&gt;What strategies have you found effective for keeping cloud costs down while maintaining scalability?&lt;/li&gt;
&lt;li&gt;How do you handle database scaling in applications with unpredictable traffic patterns?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'd love to hear about your experiences in the comments!&lt;/p&gt;

&lt;p&gt;Tags: #aws, #cloud-architecture, #serverless, #devops, #web-development, #infrastructure-as-code, #scalability&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.amazonaws.com%2Fuploads%2Farticles%2F114hytnkhrlvrqslzci0.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.amazonaws.com%2Fuploads%2Farticles%2F114hytnkhrlvrqslzci0.png" alt="Image description" width="800" height="803"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;           Final Architecture diagram
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
    </item>
    <item>
      <title># 🚀 Hosting a Static Portfolio Website on AWS with CI/CD</title>
      <dc:creator>Uri Gakuru Karanja</dc:creator>
      <pubDate>Sat, 19 Apr 2025 14:04:01 +0000</pubDate>
      <link>https://dev.to/gakuruuri/-hosting-a-static-portfolio-website-on-aws-with-cicd-31b9</link>
      <guid>https://dev.to/gakuruuri/-hosting-a-static-portfolio-website-on-aws-with-cicd-31b9</guid>
      <description>&lt;p&gt;Welcome to my first AWS deployment project! In this case study, I walk through how I built and deployed my personal portfolio site using AWS services like S3, Route 53, CloudFront, ACM, and GitHub Actions for CI/CD.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Live Website&lt;/strong&gt;: &lt;a href="https://urigakuru.uriroots.com" rel="noopener noreferrer"&gt;https://urigakuru.uriroots.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
📁 &lt;strong&gt;GitHub Repo&lt;/strong&gt;: &lt;a href="https://github.com/GakuruUri/portfolio" rel="noopener noreferrer"&gt;github.com/GakuruUri/portfolio&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Project Goal
&lt;/h2&gt;

&lt;p&gt;To build, deploy, and host a professional static portfolio website using AWS services, GitHub Actions for CI/CD, and a custom domain.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧰 Tools &amp;amp; AWS Services Used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: HTML, CSS, JS (static site)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt;: Amazon S3 (static hosting)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS&lt;/strong&gt;: Route 53&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN&lt;/strong&gt;: CloudFront&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL&lt;/strong&gt;: AWS Certificate Manager (ACM)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation&lt;/strong&gt;: GitHub Actions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Control&lt;/strong&gt;: Git + GitHub&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📐 Architecture Diagram
&lt;/h2&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.amazonaws.com%2Fuploads%2Farticles%2Faa8lqlzhr4819d5ti2j9.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.amazonaws.com%2Fuploads%2Farticles%2Faa8lqlzhr4819d5ti2j9.png" alt="Image description" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Step-by-Step Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Build Static Website
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Developed my portfolio using basic HTML/CSS/JS.&lt;/li&gt;
&lt;li&gt;Tested it locally before deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Create &amp;amp; Configure an S3 Bucket
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Created a bucket with the name matching my domain: &lt;code&gt;urigakuru.uriroots.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enabled &lt;strong&gt;static website hosting&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set index to default root object &lt;code&gt;index.html&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Configure Custom Domain with Route 53
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Registered domain: &lt;code&gt;uriroots.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Created a hosted zone and set up records to point to CloudFront.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Provision SSL Certificate with ACM
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Requested a public certificate for &lt;code&gt;*.uriroots.com&lt;/code&gt; and &lt;code&gt;uriroots.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Validated it using Route 53 DNS.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Set Up CloudFront
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Configured a CloudFront distribution pointing to the S3 bucket.&lt;/li&gt;
&lt;li&gt;Enabled redirect HTTP to HTTPS.&lt;/li&gt;
&lt;li&gt;Attached the custom SSL certificate.&lt;/li&gt;
&lt;li&gt;Set the origin domain to S3 bucket endpoint, &lt;strong&gt;not the static website hosting URL&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Configure GitHub Actions for CI/CD
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Created a &lt;code&gt;.github/workflows/cicd.yml&lt;/code&gt; file for automated deployments.&lt;/li&gt;
&lt;li&gt;Used &lt;code&gt;aws-actions/configure-aws-credentials&lt;/code&gt; to deploy to AWS.&lt;/li&gt;
&lt;li&gt;Fixed two issues:

&lt;ul&gt;
&lt;li&gt;❌ &lt;code&gt;SOURCE_DIR&lt;/code&gt; mis-indentation → ✅ Corrected line 22&lt;/li&gt;
&lt;li&gt;❌ Invalid &lt;code&gt;AWS_S3_BUCKET&lt;/code&gt; secret reference → ✅ Used correct syntax for accessing secrets&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. Trigger Deployment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Pushed code to GitHub.&lt;/li&gt;
&lt;li&gt;GitHub Actions triggered and deployed the site to S3.&lt;/li&gt;
&lt;li&gt;Confirmed deployment worked by checking the CloudFront URL and custom domain.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚠️ Challenges Faced
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Misconfiguration in GitHub Actions YAML (&lt;code&gt;SOURCE_DIR&lt;/code&gt; indentation).&lt;/li&gt;
&lt;li&gt;Incorrect reference to &lt;code&gt;AWS_S3_BUCKET&lt;/code&gt; secret in the CI/CD workflow.&lt;/li&gt;
&lt;li&gt;Learning curve around setting up CloudFront properly with HTTPS.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✅ Results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;💻 Live Website: &lt;a href="https://urigakuru.uriroots.com" rel="noopener noreferrer"&gt;https://urigakuru.uriroots.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔐 HTTPS-secured, fast-loading static portfolio site&lt;/li&gt;
&lt;li&gt;🤖 Fully automated CI/CD pipeline from GitHub to AWS&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🌱 What I Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How to serve static sites on AWS using S3 and CloudFront&lt;/li&gt;
&lt;li&gt;How to configure Route 53 and SSL certificates with ACM&lt;/li&gt;
&lt;li&gt;GitHub Actions workflow creation and debugging&lt;/li&gt;
&lt;li&gt;Importance of architecture planning and secrets management&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 Future Improvements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add a blog section (Markdown-based)&lt;/li&gt;
&lt;li&gt;Integrate analytics (e.g., AWS Pinpoint or Google Analytics)&lt;/li&gt;
&lt;li&gt;Implement deployment notifications via SNS&lt;/li&gt;
&lt;li&gt;Extend CI/CD to include staging/preview environments&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;This project helped me tie together several AWS services and sharpen my DevOps skills. It’s a strong foundation for more complex cloud-native projects.&lt;/p&gt;

&lt;p&gt;If you're a recruiter or hiring manager — I’d love to chat about cloud roles!&lt;/p&gt;




</description>
    </item>
  </channel>
</rss>
