<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[How I Designed And Built A Secure Two-Tier Architecture on GCP]]></title><description><![CDATA[How I Designed And Built A Secure Two-Tier Architecture on GCP]]></description><link>https://mikey-og.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>How I Designed And Built A Secure Two-Tier Architecture on GCP</title><link>https://mikey-og.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 08 Sep 2026 05:07:13 GMT</lastBuildDate><atom:link href="https://mikey-og.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I Designed and Built a Secure Two-Tier Architecture on Google Cloud Platform]]></title><description><![CDATA[A hands-on cloud engineering project demonstrating secure networking, high availability, load balancing, object storage, and infrastructure design using Google Cloud Platform.




Introduction
Imagine]]></description><link>https://mikey-og.hashnode.dev/how-i-designed-and-built-a-secure-two-tier-architecture-on-google-cloud-platform</link><guid isPermaLink="true">https://mikey-og.hashnode.dev/how-i-designed-and-built-a-secure-two-tier-architecture-on-google-cloud-platform</guid><category><![CDATA[Devops]]></category><category><![CDATA[CloudEngineer]]></category><category><![CDATA[GCP]]></category><category><![CDATA[Google]]></category><category><![CDATA[AWS]]></category><dc:creator><![CDATA[Mikey]]></dc:creator><pubDate>Fri, 31 Jul 2026 15:02:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/eda73a02-ed56-46fc-b620-e8873be475cd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><em>A hands-on cloud engineering project demonstrating secure networking, high availability, load balancing, object storage, and infrastructure design using Google Cloud Platform.</em></p>
</blockquote>
<hr />
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/a15c81d8-d565-42ff-8583-f716304a5561.png" alt="" style="display:block;margin:0 auto" />

<h2>Introduction</h2>
<p>Imagine FreshCart, a rapidly growing e-commerce startup, announcing a weekend flash sale. Thousands of shoppers begin visiting the website simultaneously, expecting a smooth shopping experience.</p>
<p>Now imagine the entire application runs on a single virtual machine that's directly connected to the Internet.</p>
<p>That single server becomes responsible for handling incoming traffic, serving application requests, performing operating system updates, and protecting itself from attackers—all while trying to stay online.</p>
<p>It only takes one unexpected traffic spike, hardware issue, or security vulnerability to bring everything down.</p>
<p>As FreshCart's newly hired cloud engineer, my goal was to redesign this architecture into something more secure, scalable, and production-ready.</p>
<p>Rather than exposing the backend server directly to the Internet, I built a <strong>two-tier architecture</strong> where:</p>
<ul>
<li><p>users only communicate with a public load balancer,</p>
</li>
<li><p>backend compute resources remain isolated inside private networking,</p>
</li>
<li><p>static website assets are served separately from object storage,</p>
</li>
<li><p>HTTPS is enforced using managed SSL certificates,</p>
</li>
<li><p>and storage costs are automatically optimized through lifecycle management.</p>
</li>
</ul>
<p>This project was built entirely on <strong>Google Cloud Platform (GCP)</strong> using the <code>gcloud</code> <strong>CLI</strong>, with security, availability, and operational simplicity as the primary design goals.</p>
<hr />
<h1>The Problem with a Single Public VM</h1>
<p>At first glance, deploying everything on one virtual machine seems simple.</p>
<p>However, this architecture creates several problems.</p>
<h3>Security</h3>
<p>A publicly accessible VM increases the attack surface considerably. Even with firewall rules in place, a misconfiguration could expose SSH or application ports to the Internet.</p>
<h3>Availability</h3>
<p>A single VM represents a single point of failure.</p>
<p>If the instance crashes, undergoes maintenance, or experiences an outage, the entire application becomes unavailable.</p>
<h3>Scalability</h3>
<p>As traffic grows, scaling becomes increasingly difficult because all requests are handled by one server.</p>
<p>Introducing additional instances later requires redesigning the networking from scratch.</p>
<h3>Maintainability</h3>
<p>Serving static website files from the same machine as the backend application mixes responsibilities that should remain independent.</p>
<p>Modern cloud architectures separate these concerns to improve security and operational efficiency.</p>
<p>These limitations motivated the architecture shown below.</p>
<hr />
<h1>Planning Before Building</h1>
<p>Before opening Cloud Shell or running a single <code>gcloud</code> command, I first sketched the intended architecture.</p>
<p>The primary design objectives were:</p>
<ul>
<li><p>isolate backend infrastructure from the Internet</p>
</li>
<li><p>expose only one secure public entry point</p>
</li>
<li><p>allow backend servers to access updates without public IP addresses</p>
</li>
<li><p>separate static content from application traffic</p>
</li>
<li><p>prepare the network for future scaling across multiple zones</p>
</li>
</ul>
<h2>Initial Architecture Sketch</h2>
<blockquote>
<p><strong>Initial architecture concept</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/c472ca70-7a45-4404-b879-84780974257e.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<p>During implementation, the overall design remained largely unchanged, although a few networking details—such as firewall rules for Google Load Balancer health checks and managed certificate provisioning—were refined after testing.</p>
<hr />
<h1>Final Architecture</h1>
<p>The completed architecture consists of two distinct layers.</p>
<ul>
<li><p><strong>Public Layer</strong></p>
<ul>
<li><p>HTTPS Load Balancer</p>
</li>
<li><p>Google Cloud Storage Static Website</p>
</li>
<li><p>Managed SSL Certificate</p>
</li>
</ul>
</li>
<li><p><strong>Private Layer</strong></p>
<ul>
<li><p>Custom VPC</p>
</li>
<li><p>Two private subnets</p>
</li>
<li><p>Backend VM</p>
</li>
<li><p>Cloud Router</p>
</li>
<li><p>Cloud NAT</p>
</li>
</ul>
</li>
</ul>
<h2>Final Network Architecture Diagram</h2>
<blockquote>
<p><strong>Architecture Diagram</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/6f61ab0e-33de-43ec-b20d-ba6cecbd43e6.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<h3>How Traffic Flows</h3>
<ol>
<li><p>A user opens the FreshCart website.</p>
</li>
<li><p>Requests to the marketing website are served directly from Google Cloud Storage over HTTPS.</p>
</li>
<li><p>Requests to the backend application first arrive at the Google Cloud HTTP(S) Load Balancer.</p>
</li>
<li><p>The load balancer terminates HTTPS before forwarding traffic to the backend VM using HTTP.</p>
</li>
<li><p>The backend VM processes the request entirely inside the private subnet.</p>
</li>
<li><p>Whenever the VM needs to download operating system updates or install packages, outbound traffic is routed through Cloud NAT.</p>
</li>
</ol>
<p>At no point is the backend VM directly reachable from the Internet.</p>
<hr />
<h1>Building the Infrastructure</h1>
<p>After finalizing the design, I implemented each component using the Google Cloud CLI.</p>
<p>Rather than dumping every command used, I'll highlight a few that illustrate the major architectural decisions.</p>
<hr />
<h2>1. Creating the Custom VPC</h2>
<p>I began by creating a dedicated Virtual Private Cloud rather than relying on GCP's default network.</p>
<p>This gave me complete control over subnets, firewall rules, routing, and IP ranges.</p>
<blockquote>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/9cb7badc-b0fb-4f4e-badc-d6c3f239ce9b.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<hr />
<h2>2. Creating Two Private Subnets</h2>
<p>Two private subnets were created across separate zones.</p>
<p>Although only one backend VM was deployed for this project, designing the network across multiple zones means future scaling can happen without redesigning the infrastructure.</p>
<blockquote>
<p><strong>Subnets</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/fcf66597-469a-4b8b-a57a-802a77ee562b.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<hr />
<h2>3. Deploying the Backend VM</h2>
<p>One of the most important decisions was deploying the backend server <strong>without a public IP address</strong>.</p>
<p>A simplified version of the deployment command looked like this:</p>
<pre><code class="language-bash">gcloud compute instances create freshcart-backend \
    --no-address
</code></pre>
<p>That single flag (<code>--no-address</code>) ensures the VM cannot be reached directly from the public Internet.</p>
<p>Instead, all traffic must pass through the load balancer.</p>
<p>The backend application itself was a simple Nginx server returning a placeholder response.</p>
<blockquote>
<p><strong>Backend VM</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/42b5a88b-f48b-4f0f-8b28-73eb32477589.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<blockquote>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/05abd2e8-e152-41a9-828f-f526d6631776.jpg" alt="" style="display:block;margin:0 auto" />

<p><strong>"FreshCart backend is up"</strong></p>
</blockquote>
<hr />
<h2>4. Configuring Cloud NAT</h2>
<p>Removing the VM's public IP introduced another challenge.</p>
<p>Without Internet access, package installation and system updates would fail.</p>
<p>To solve this, I configured Cloud Router together with Cloud NAT.</p>
<p>Cloud NAT allows outbound Internet access while preventing any inbound connections.</p>
<p>This preserves security without sacrificing maintainability.</p>
<blockquote>
<p><strong>Cloud NAT</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/ab3a27d1-f070-43af-bf95-97f41a570d4b.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<hr />
<h2>5. Configuring the Load Balancer</h2>
<p>The Google Cloud HTTP(S) Load Balancer became the only public entry point into the backend application.</p>
<p>It performs several important functions:</p>
<ul>
<li><p>receives Internet traffic</p>
</li>
<li><p>terminates HTTPS</p>
</li>
<li><p>performs health checks</p>
</li>
<li><p>forwards requests only to healthy backend instances</p>
</li>
</ul>
<p>This means unhealthy instances never receive production traffic.</p>
<blockquote>
<p><strong>Load Balancer</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/6e728ae4-664f-4a08-a313-59f0c0e3da5e.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<blockquote>
<p><strong>Backend Service</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/6ba66323-c28f-430f-a34f-da3776bc9927.jpg" alt="" style="display:block;margin:0 auto" />

<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/a3c613c8-951f-4c3c-abdf-bc4d2971ceab.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<hr />
<h2>6. Hosting the Static Website</h2>
<p>Instead of serving the landing page from the VM, I deployed it using Google Cloud Storage's static website hosting.</p>
<p>This approach offers several advantages:</p>
<ul>
<li><p>lower operational cost</p>
</li>
<li><p>no server maintenance</p>
</li>
<li><p>high durability</p>
</li>
<li><p>excellent performance</p>
</li>
</ul>
<p>The website consisted of a simple "FreshCart Coming Soon" landing page.</p>
<blockquote>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/2e10baff-6441-4f72-98ad-f2a4d4e4a7c1.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<hr />
<h2>7. Configuring HTTPS and a Custom Domain</h2>
<p>To simulate a production deployment without purchasing a domain, I used DuckDNS.</p>
<p>After reserving a static IP address for the load balancer, I pointed my DuckDNS subdomain to that address.</p>
<p>Google's managed certificate automatically validated ownership through DNS before issuing the SSL certificate.</p>
<p>Once provisioning completed, the website became accessible over HTTPS.</p>
<blockquote>
<p><strong>DuckDNS configuration</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/317b627d-ca4e-451d-adae-a931d662cb31.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<blockquote>
<p><strong>Managed SSL Certificate</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/e15d5d6a-b623-470e-a4f4-b121e3813df1.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<hr />
<h2>8. Configuring Storage Lifecycle Management</h2>
<p>Static assets rarely need to remain in premium storage forever.</p>
<p>To reduce storage costs automatically, I configured a lifecycle policy that:</p>
<ul>
<li><p>moves objects older than 30 days into Nearline Storage</p>
</li>
<li><p>permanently deletes objects older than 90 days</p>
</li>
</ul>
<p>A simplified version of the lifecycle configuration looked like this:</p>
<pre><code class="language-json">{
  "rule": [
    {
      "action": {
        "type": "SetStorageClass",
        "storageClass": "NEARLINE"
      },
      "condition": {
        "age": 30
      }
    },
    {
      "action": {
        "type": "Delete"
      },
      "condition": {
        "age": 90
      }
    }
  ]
}
</code></pre>
<p>This keeps storage costs predictable without requiring manual cleanup.</p>
<blockquote>
<p><strong>Lifecycle Policy</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/a76ac587-fbb1-4de4-9f7c-81114769f6c1.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<blockquote>
<img src="https://cdn.hashnode.com/uploads/covers/6a48d7176bfa274b91f65309/782b105b-bbae-4906-b2db-310a31f67119.jpg" alt="" style="display:block;margin:0 auto" /></blockquote>
<hr />
<h1>The Three Hardest Engineering Decisions</h1>
<p>Cloud engineering is rarely about choosing the "right" service.</p>
<p>More often, it's about making thoughtful trade-offs.</p>
<h2>Decision 1 — Keeping the Backend Private</h2>
<p>The easiest approach would have been assigning the VM a public IP and restricting access with firewall rules.</p>
<p>Instead, I chose to remove the public IP entirely.</p>
<h3>Why?</h3>
<p>Even the best firewall rules can be accidentally modified.</p>
<p>A server without a public IP simply cannot be attacked directly from the Internet.</p>
<h3>Trade-off</h3>
<p>This required adding Cloud NAT, increasing infrastructure cost slightly.</p>
<p>For production workloads, that additional cost is well worth the significant security improvement.</p>
<hr />
<h2>Decision 2 — Designing for Multiple Zones</h2>
<p>Although only one VM was required for this project, I still created subnets across different zones.</p>
<h3>Why?</h3>
<p>Infrastructure should be designed for growth.</p>
<p>If FreshCart later experiences increased traffic, a Managed Instance Group can distribute instances across zones without rebuilding the network.</p>
<h3>Trade-off</h3>
<p>The architecture is slightly more complex today but significantly easier to scale tomorrow.</p>
<hr />
<h2>Decision 3 — Separating Static Content from Compute</h2>
<p>Rather than serving the marketing website from Nginx, I hosted it using Google Cloud Storage.</p>
<h3>Why?</h3>
<p>Static websites don't require virtual machines.</p>
<p>Object storage is cheaper, highly durable, and eliminates server maintenance entirely.</p>
<h3>Trade-off</h3>
<p>Dynamic applications still require compute resources, but static assets benefit greatly from object storage.</p>
<hr />
<h1>Challenges I Encountered</h1>
<p>Like most cloud projects, not everything worked perfectly on the first attempt.</p>
<h2>Managed Certificate Provisioning</h2>
<p>The SSL certificate remained in the <strong>PROVISIONING</strong> state for several minutes.</p>
<p>After verifying my DuckDNS records and waiting for DNS propagation, Google successfully completed validation and issued the certificate.</p>
<hr />
<h2>Load Balancer Health Checks</h2>
<p>Initially, the backend service remained unhealthy.</p>
<p>The issue turned out to be missing firewall rules allowing Google's health check IP ranges to reach the backend instance.</p>
<p>Once those rules were added, the backend immediately became healthy.</p>
<hr />
<h2>Private VM Connectivity</h2>
<p>Removing the VM's public IP meant package installation failed initially.</p>
<p>Deploying Cloud NAT restored outbound connectivity while maintaining complete inbound isolation.</p>
<hr />
<h1>What I Would Add Next</h1>
<p>If FreshCart continued growing, there are several improvements I would implement.</p>
<h3>Managed Instance Groups</h3>
<p>Replace the single backend VM with a regional Managed Instance Group capable of automatic scaling.</p>
<h3>Cloud CDN</h3>
<p>Serve static website assets from Google's edge locations around the world to reduce latency.</p>
<h3>Cloud Armor</h3>
<p>Protect the application from DDoS attacks and common web exploits using Google's Web Application Firewall.</p>
<h3>Cloud Monitoring and Logging</h3>
<p>Implement dashboards, uptime monitoring, alerting, and centralized logging using Cloud Monitoring and Cloud Logging.</p>
<h3>Infrastructure as Code</h3>
<p>Convert every CLI command into reusable Terraform modules, enabling version control, code reviews, and automated deployments.</p>
<hr />
<h1>Lessons Learned</h1>
<p>This project reinforced several important cloud engineering principles.</p>
<p>Security begins with network design rather than firewall rules alone.</p>
<p>Separating public and private infrastructure significantly reduces attack surfaces while making future scaling much easier.</p>
<p>Building with managed cloud services—such as Load Balancing, Cloud NAT, Cloud Storage, and managed SSL certificates—also reduces operational overhead compared to managing everything manually.</p>
<p>Perhaps the biggest takeaway was that good architecture isn't just about getting resources running. It's about making thoughtful decisions that balance security, cost, scalability, and maintainability from the very beginning.</p>
<hr />
<h1>Conclusion</h1>
<p>Designing FreshCart's two-tier architecture gave me practical experience implementing secure networking patterns commonly found in production cloud environments.</p>
<p>Starting from a simple problem—protecting a backend application from direct Internet exposure—I built an architecture that isolates compute resources, centralizes public access through a load balancer, serves static content efficiently from object storage, automates storage lifecycle management, and prepares the infrastructure for future growth.</p>
<p>While this project fulfills the requirements of the capstone, it also demonstrates an approach to cloud architecture that prioritizes security by default, operational simplicity, and scalability. These are principles I intend to carry into every future cloud engineering project.</p>
<hr />
<h2>GitHub Repository</h2>
<blockquote>
<p><a href="https://github.com/Mikey064/freshcart-twotier">https://github.com/Mikey064/freshcart-twotier</a></p>
</blockquote>
<hr />
<h2>References</h2>
<ul>
<li><p>Google Cloud Documentation</p>
</li>
<li><p>Google Cloud Load Balancing Documentation</p>
</li>
<li><p>Google Cloud Storage Documentation</p>
</li>
<li><p>Cloud NAT Documentation</p>
</li>
<li><p>Google Cloud Managed SSL Certificates Documentation</p>
</li>
<li><p>DuckDNS Documentation</p>
</li>
</ul>
]]></content:encoded></item></channel></rss>