<?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[GKE Deployment for BidStream MERN App]]></title><description><![CDATA[GKE Deployment for BidStream MERN App]]></description><link>https://bidstream-deployment-gke-k8s.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 31 Aug 2026 22:33:52 GMT</lastBuildDate><atom:link href="https://bidstream-deployment-gke-k8s.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA["The Silent AWS Bill Killer: How NAT Gateway Was Eating My S3 Costs (And the 10-Minute Fix)"]]></title><description><![CDATA[If your workloads live in private subnets and talk to Amazon S3, there's a good chance you're paying a "hidden tax" every single day — and it's not on the S3 bill. It's on your NAT Gateway line item.
]]></description><link>https://bidstream-deployment-gke-k8s.hashnode.dev/the-silent-aws-bill-killer-how-nat-gateway-was-eating-my-s3-costs-and-the-10-minute-fix</link><guid isPermaLink="true">https://bidstream-deployment-gke-k8s.hashnode.dev/the-silent-aws-bill-killer-how-nat-gateway-was-eating-my-s3-costs-and-the-10-minute-fix</guid><dc:creator><![CDATA[Piyush Shrivastava]]></dc:creator><pubDate>Fri, 24 Jul 2026 11:45:44 GMT</pubDate><content:encoded><![CDATA[<p>If your workloads live in private subnets and talk to Amazon S3, there's a good chance you're paying a "hidden tax" every single day — and it's not on the S3 bill. It's on your NAT Gateway line item.</p>
<p>Here's the story of how a one-time VPC networking tweak cut a real workload's daily AWS bill by up to 40%, with zero code changes and zero downtime.</p>
<h2>The Problem Nobody Notices Until the Bill Arrives</h2>
<p>By default, when an EC2 instance or EKS node sitting in a <strong>private subnet</strong> talks to S3, the traffic doesn't magically float into AWS's internal network. It takes the only path it knows:</p>
<img src="https://cdn.hashnode.com/uploads/covers/687505d48024d328b4217e48/e4b8aea9-2c7a-4cd8-9e9a-308ed82cfb35.gif" alt="" style="display:block;margin:0 auto" />

<pre><code class="language-plaintext">Private Subnet → NAT Gateway → Internet Gateway → Amazon S3
</code></pre>
<p>That NAT Gateway hop looks harmless. It isn't. AWS charges <strong>NAT Gateway data processing fees per GB</strong>, in both directions, for every byte that passes through it — including all your S3 uploads, downloads, log shipping, backups, and model/artifact pulls. It's traffic that never needed to leave the AWS network in the first place, and you're paying internet-gateway-style tolls for an internal AWS-to-AWS conversation.</p>
<p>The worst part? It's invisible until someone actually breaks down the Cost Explorer by usage type. It just looks like "NAT Gateway costs are high" with no obvious smoking gun — because the smoking gun is S3 traffic disguised as generic data processing.</p>
<h2>The Fix: An S3 Gateway VPC Endpoint</h2>
<p>Enter the <strong>Amazon S3 Gateway VPC Endpoint</strong> — a free, fully AWS-managed endpoint type that lets resources in your VPC reach S3 directly over AWS's private backbone, completely bypassing the NAT Gateway and the public internet.</p>
<img src="https://cdn.hashnode.com/uploads/covers/687505d48024d328b4217e48/b1bea4d8-8247-499f-95b1-fe9c1eac2b6c.gif" alt="" style="display:block;margin:0 auto" />

<pre><code class="language-plaintext">Private Subnet → S3 Gateway VPC Endpoint → Amazon S3
</code></pre>
<p>No NAT hop. No internet routing. No per-GB NAT data processing charge for that traffic. Just a route table entry pointing at an AWS-managed prefix list for S3.</p>
<p>And here's the kicker: <strong>Gateway endpoints for S3 and DynamoDB are free.</strong> You pay nothing to create or use them — you're only removing a cost, not adding one.</p>
<h2>The Real Numbers</h2>
<p>This isn't theoretical. Here's what one production workload (EKS nodes + EC2 instances hitting S3 heavily) looked like before and after:</p>
<table>
<thead>
<tr>
<th></th>
<th>Before (via NAT Gateway)</th>
<th>After (via S3 Gateway Endpoint)</th>
</tr>
</thead>
<tbody><tr>
<td>Daily cost</td>
<td>$61 – $63</td>
<td>$39 – $45</td>
</tr>
<tr>
<td><strong>Daily savings</strong></td>
<td>N/A</td>
<td><strong>≈ $18 – $24</strong></td>
</tr>
<tr>
<td><strong>Monthly savings</strong></td>
<td>N/A</td>
<td><strong>≈ $540 – $720</strong></td>
</tr>
</tbody></table>
<p>That's roughly <strong>$6,500 – $8,600 a year</strong>, recovered from a single networking change, on one workload. Scale that across every private-subnet workload in your account talking to S3, and the number gets uncomfortable fast if you <em>haven't</em> done this yet.</p>
<h2>How to Set It Up (Takes About 10 Minutes)</h2>
<h3>Step 1: Create the VPC Endpoint</h3>
<p>In the VPC console, go to <strong>Endpoints → Create endpoint</strong>.</p>
<h3>Step 2: Configure it for S3</h3>
<ul>
<li><p>Give it a name (e.g. <code>vpc_gateway_endpoint</code>)</p>
</li>
<li><p>Under <strong>Service category</strong>, choose <strong>AWS services</strong></p>
</li>
<li><p>Search for and select: <code>com.amazonaws.&lt;your-region&gt;.s3</code></p>
</li>
<li><p>Type: <strong>Gateway</strong> (not Interface — this distinction matters, Gateway is the free, route-table-based option)</p>
</li>
</ul>
<h3>Step 3: Network configuration</h3>
<ul>
<li><p>Select your VPC</p>
</li>
<li><p>Select the <strong>route tables</strong> associated with the private subnets that need S3 access (public route tables too, if relevant)</p>
</li>
</ul>
<h3>Step 4: Verify the route</h3>
<p>Once the endpoint is created, check the route tables you selected. You should see a new route:</p>
<ul>
<li><p><strong>Destination:</strong> the AWS-managed prefix list for S3 (see <a href="https://docs.aws.amazon.com/vpc/latest/userguide/working-with-aws-managed-prefix-lists.html#available-aws-managed-prefix-lists">AWS's prefix list docs</a>)</p>
</li>
<li><p><strong>Target:</strong> your new VPC endpoint</p>
</li>
</ul>
<p>That's it. No application changes, no redeploys, no DNS changes required — S3 requests from resources in those subnets now silently route over the AWS backbone instead of through NAT.</p>
<h2>Why This Matters Beyond the Dollar Amount</h2>
<ul>
<li><p><strong>Security:</strong> traffic never touches the public internet path, reducing your exposure surface.</p>
</li>
<li><p><strong>Reliability:</strong> one less hop, one less dependency on NAT Gateway throughput/bandwidth limits.</p>
</li>
<li><p><strong>Scalability:</strong> as your S3 traffic grows (logs, backups, model artifacts, data lake reads), your NAT bill <em>stops growing with it</em> — because that traffic isn't hitting NAT anymore.</p>
</li>
</ul>
<h2>The Takeaway</h2>
<p>If you've got EKS nodes, EC2 instances, Lambda-in-VPC, or literally anything in a private subnet reading/writing S3 — and you haven't set up a Gateway VPC Endpoint — you are almost certainly paying NAT Gateway tolls on traffic that could be free and faster.</p>
<p>Go check your route tables. It's a 10-minute fix that could quietly hand you back hundreds of dollars a month.</p>
<hr />
<p><em>Have you found other "hidden" AWS cost traps like this one? Drop them in the comments — I'd love to compare notes.</em></p>
]]></content:encoded></item><item><title><![CDATA[🔥 From Zero to GitOps: Deploying a MERN App on GKE with ArgoCD + SSL + Ingress]]></title><description><![CDATA[🚀 How I Deployed a MERN App on GKE with ArgoCD & Kubernetes Dashboard (Beginner Friendly Guide)
If you’ve ever tried deploying a MERN app (MongoDB + Express + React + Node) to the cloud, you know the pain. 😵‍💫But I wanted production-level scalabil...]]></description><link>https://bidstream-deployment-gke-k8s.hashnode.dev/from-zero-to-gitops-deploying-a-mern-app-on-gke-with-argocd-ssl-ingress</link><guid isPermaLink="true">https://bidstream-deployment-gke-k8s.hashnode.dev/from-zero-to-gitops-deploying-a-mern-app-on-gke-with-argocd-ssl-ingress</guid><category><![CDATA[Cloud Computing]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Devops articles]]></category><category><![CDATA[DevOps Journey]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[google cloud]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Piyush Shrivastava]]></dc:creator><pubDate>Mon, 15 Sep 2025 16:52:50 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757954913326/7ba41137-5209-4009-ba22-9be1613c7e33.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-how-i-deployed-a-mern-app-on-gke-with-argocd-amp-kubernetes-dashboard-beginner-friendly-guide">🚀 How I Deployed a MERN App on GKE with ArgoCD &amp; Kubernetes Dashboard (Beginner Friendly Guide)</h2>
<p>If you’ve ever tried deploying a <strong>MERN app (MongoDB + Express + React + Node)</strong> to the cloud, you know the pain. 😵‍💫<br />But I wanted <strong>production-level scalability + GitOps magic</strong>, so I challenged myself:</p>
<p>👉 Deploy my project <strong>BidStream (a MERN-based bidding app)</strong> on <strong>Google Kubernetes Engine (GKE)</strong> with:</p>
<ul>
<li><p>Terraform (infra as code)</p>
</li>
<li><p>Docker + Kubernetes manifests</p>
</li>
<li><p>NGINX Ingress + SSL via Let’s Encrypt</p>
</li>
<li><p>GitOps with <strong>ArgoCD</strong></p>
</li>
<li><p>A shiny <strong>Kubernetes Dashboard</strong> exposed securely</p>
</li>
</ul>
<p>…and yes, it worked 🎉</p>
<p>Here’s the exact step-by-step journey (newbie-friendly). Let’s go 👇</p>
<hr />
<h2 id="heading-step-1-setting-up-gke-cluster">🏗️ Step 1: Setting up GKE Cluster</h2>
<ul>
<li><p>I spun up a <strong>GCP Standard GKE Cluster</strong> via Terraform.</p>
</li>
<li><p>Think of it as: one command → entire Kubernetes infra ready (nodes, networking, RBAC).</p>
</li>
<li><p>If you don’t know Terraform, you can just use <code>gcloud container clusters create</code>.</p>
</li>
</ul>
<p>💡 Screenshot of my cluster (flexing moment 😎):  </p>
<p><img src="https://github.com/user-attachments/assets/a56a5bc4-f527-4307-8764-28e6cac17878" alt="gke-cluster" /></p>
<hr />
<h2 id="heading-step-2-dockerizing-backend-frontend">🐳 Step 2: Dockerizing Backend + Frontend</h2>
<p>Both my <strong>backend (Express API)</strong> and <strong>frontend (React/Vite app)</strong> had their own <code>Dockerfile</code>.</p>
<p>Example backend build:</p>
<pre><code class="lang-plaintext">cd backend
cp .env.docker .env
docker build -t gcr.io/YOUR_PROJECT_ID/backend:latest .
docker push gcr.io/YOUR_PROJECT_ID/backend:latest
</code></pre>
<p>Same for frontend.<br />✅ Images pushed to Google Container Registry (GCR).</p>
<hr />
<h2 id="heading-step-3-nginx-ingress-domain-setup">🌐 Step 3: NGINX Ingress + Domain Setup</h2>
<p>Installed <strong>Ingress Controller</strong> with Helm:</p>
<pre><code class="lang-plaintext">helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm upgrade --install ingress-nginx ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.service.type=LoadBalancer
</code></pre>
<p>Then pointed my domain <code>piyush-web-app.co.in</code> → LoadBalancer IP with DNS A record.<br />Boom 💥 My app had a domain!</p>
<hr />
<h2 id="heading-step-4-tls-ssl-with-lets-encrypt">🔒 Step 4: TLS SSL with Let’s Encrypt</h2>
<p>I added a <strong>ClusterIssuer</strong> for SSL:</p>
<pre><code class="lang-plaintext">kubectl apply -f k8s/cluster-issuer.yaml
</code></pre>
<p>Then updated <code>ingress.yaml</code> to include:</p>
<pre><code class="lang-plaintext">annotations:
  cert-manager.io/cluster-issuer: letsencrypt-prod
</code></pre>
<p>Result → <strong>Auto HTTPS with Let’s Encrypt</strong> 🔐</p>
<hr />
<h2 id="heading-step-5-deploying-mern-app-on-kubernetes">🚀 Step 5: Deploying MERN App on Kubernetes</h2>
<p>Applied backend + frontend deployments:</p>
<pre><code class="lang-plaintext">kubectl apply -f k8s/backend-deployment.yaml
kubectl apply -f k8s/frontend-deployment.yaml
kubectl apply -f k8s/ingress.yaml
</code></pre>
<p>🎉 The app was LIVE at <a target="_blank" href="https://piyush-web-app.co.in">https://piyush-web-app.co.in</a>.</p>
<hr />
<h2 id="heading-step-6-gitops-with-argocd-my-fav-part">🤖 Step 6: GitOps with ArgoCD (My Fav Part ❤️)</h2>
<p>Instead of <code>kubectl apply</code> every time → I wanted <strong>automated sync from GitHub</strong>.</p>
<h3 id="heading-install-argocd">Install ArgoCD</h3>
<pre><code class="lang-plaintext">kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
</code></pre>
<p>Expose service as LoadBalancer:</p>
<pre><code class="lang-plaintext">kubectl patch svc argocd-server -n argocd \
  -p '{"spec": {"type": "LoadBalancer"}}'
</code></pre>
<h3 id="heading-login-to-argocd">Login to ArgoCD</h3>
<p>Get initial password:</p>
<pre><code class="lang-plaintext">kubectl get secret argocd-initial-admin-secret -n argocd \
  -o jsonpath="{.data.password}" | base64 -d
</code></pre>
<p>Access via:<br /><code>https://&lt;ARGOCD-LB-IP&gt;</code> → login with <code>admin</code> + password.</p>
<h3 id="heading-create-application-bidstream">Create Application (BidStream)</h3>
<pre><code class="lang-plaintext">apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: bidstream
  namespace: argocd
spec:
  project: default
  source:
    repoURL: 'https://github.com/YOUR_USERNAME/YOUR_REPO.git'
    targetRevision: main
    path: k8s
  destination:
    server: https://kubernetes.default.svc
    namespace: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
</code></pre>
<p>Apply it → ArgoCD pulls manifests from <code>k8s/</code> dir in GitHub → auto deploys.<br />🔥 GitOps in action!</p>
<hr />
<h2 id="heading-step-7-kubernetes-dashboard">📊 Step 7: Kubernetes Dashboard</h2>
<p>Deployed dashboard + exposed it with LoadBalancer:</p>
<pre><code class="lang-plaintext">kubectl patch svc kubernetes-dashboard -n kubernetes-dashboard \
  -p '{"spec": {"type": "LoadBalancer"}}'
</code></pre>
<p>Created admin-user + token → logged in 🎉</p>
<p>UI screenshot:  </p>
<p><img src="https://github.com/user-attachments/assets/d40088b3-f594-42cb-a26a-f8ed37af1fc2" alt="k8s-dashboard" /></p>
<hr />
<h2 id="heading-final-result">🎯 Final Result</h2>
<ul>
<li><p><strong>Frontend</strong> → <a target="_blank" href="https://piyush-web-app.co.in">https://piyush-web-app.co.in</a></p>
</li>
<li><p><strong>Backend</strong> → <code>/api</code> behind ingress</p>
</li>
<li><p><strong>K8s Dashboard</strong> → LB endpoint with token login</p>
</li>
<li><p><strong>ArgoCD</strong> → Auto-syncing my repo with cluster</p>
</li>
</ul>
<hr />
<h2 id="heading-key-learnings">💡 Key Learnings</h2>
<ul>
<li><p><strong>ArgoCD is a game-changer</strong> → no more manual <code>kubectl apply</code>.</p>
</li>
<li><p><strong>Ingress + Let’s Encrypt</strong> = painless SSL setup.</p>
</li>
<li><p><strong>Terraform + GKE</strong> = infra you can recreate anywhere.</p>
</li>
</ul>
<hr />
<h2 id="heading-why-share-this">🔥 Why Share This?</h2>
<p>Because when I started, Kubernetes + GitOps felt overwhelming.<br />But trust me → if I can do it with a MERN app, so can you. 🙌</p>
<p>If you found this useful, drop a ❤️ and share it with your DevOps buddies.<br />Let’s make <strong>GitOps + Kubernetes</strong> less scary for newbies 🚀</p>
<hr />
<p>👉 Full repo with manifests: <a target="_blank" href="https://github.com/Piyushshrii/Mern_bidding_BidStream">GitHub - BidStream</a> ( Head here for more refined Instruction for deployment )</p>
]]></content:encoded></item></channel></rss>