<?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: Durrell  Gemuh</title>
    <description>The latest articles on DEV Community by Durrell  Gemuh (@durrello).</description>
    <link>https://dev.to/durrello</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3446943%2F8f5d6b41-7e53-44bd-a486-a462d19152bc.jpeg</url>
      <title>DEV Community: Durrell  Gemuh</title>
      <link>https://dev.to/durrello</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/durrello"/>
    <language>en</language>
    <item>
      <title>EKS vs ECS vs Fargate: Choosing AWS Container Compute</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Tue, 01 Sep 2026 23:53:23 +0000</pubDate>
      <link>https://dev.to/durrello/eks-vs-ecs-vs-fargate-choosing-aws-container-compute-55a7</link>
      <guid>https://dev.to/durrello/eks-vs-ecs-vs-fargate-choosing-aws-container-compute-55a7</guid>
      <description>&lt;p&gt;"Should we use EKS?" is one of the most over-answered-with-yes questions in AWS. Kubernetes is&lt;br&gt;
powerful, but it's not free, in money or operational effort. Here's how I actually decide between&lt;br&gt;
ECS, EKS, and Fargate.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, separate two questions
&lt;/h2&gt;

&lt;p&gt;These are different choices people conflate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Orchestrator:&lt;/strong&gt; ECS or EKS (Kubernetes)?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute mode:&lt;/strong&gt; EC2 (you manage nodes) or Fargate (serverless, AWS manages nodes)?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You pick one from each. "Fargate" isn't an alternative to EKS, you can run &lt;strong&gt;EKS on Fargate&lt;/strong&gt; or&lt;br&gt;
&lt;strong&gt;ECS on Fargate&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ECS: the pragmatic default
&lt;/h2&gt;

&lt;p&gt;Amazon ECS is AWS's own orchestrator. It's simpler, deeply integrated, and has no control-plane&lt;br&gt;
cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose ECS when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want to run containers without learning Kubernetes.&lt;/li&gt;
&lt;li&gt;Your team is small and values low operational overhead.&lt;/li&gt;
&lt;li&gt;You're all-in on AWS and don't need portability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; less ecosystem (no Helm, no Operators, fewer third-party tools) and it's AWS-only.&lt;/p&gt;

&lt;h2&gt;
  
  
  EKS: when you need Kubernetes specifically
&lt;/h2&gt;

&lt;p&gt;Amazon EKS is managed Kubernetes. You get the entire K8s ecosystem, Helm, ArgoCD, Operators,&lt;br&gt;
service meshes, and portability across clouds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose EKS when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You already have Kubernetes expertise or need its ecosystem (GitOps, Operators, mesh).&lt;/li&gt;
&lt;li&gt;You want workload portability across clouds/on-prem.&lt;/li&gt;
&lt;li&gt;You're running enough services that the platform investment pays off.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; real operational weight (upgrades, add-ons, RBAC, networking) and a control-plane&lt;br&gt;
cost. Don't adopt EKS for three services, that's over-engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  EC2 vs Fargate (the compute mode)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fargate (serverless):&lt;/strong&gt; no nodes to patch, scale, or capacity-plan. Pay per task's CPU/memory.
Best for variable, bursty, or low-ops workloads. Slightly higher per-unit cost; cold-ish starts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EC2:&lt;/strong&gt; you manage the nodes, but get cheaper steady-state cost, GPUs, DaemonSets, and full
control. Best for high, steady load or special requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A common sweet spot: &lt;strong&gt;Fargate for spiky/low-volume services, EC2 node groups for the steady base.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A decision shortcut
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Need the Kubernetes ecosystem or multi-cloud portability?
  ├─ Yes → EKS
  │        ├─ Small/variable workloads → EKS on Fargate
  │        └─ Steady/large or special (GPU, DaemonSets) → EKS on EC2
  └─ No  → ECS
           ├─ Low ops / variable → ECS on Fargate
           └─ Cost-sensitive steady load → ECS on EC2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cost reality
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Control plane:&lt;/strong&gt; ECS is free; EKS charges per cluster-hour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute:&lt;/strong&gt; EC2 is cheaper per unit at steady load; Fargate wins when things are idle/bursty
(no paying for empty nodes).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hidden cost:&lt;/strong&gt; EKS's biggest cost is often &lt;em&gt;engineering time&lt;/em&gt;, not the invoice. Factor it in.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The honest recommendation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Most teams under ~20 services, AWS-only:&lt;/strong&gt; start with &lt;strong&gt;ECS on Fargate&lt;/strong&gt;. It's boring,
cheap to operate, and ships fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams that need K8s' ecosystem or run at real scale:&lt;/strong&gt; &lt;strong&gt;EKS&lt;/strong&gt;, with Fargate for variable
workloads and EC2 for the steady base.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't pick EKS because it's on your CV.&lt;/strong&gt; Pick it because you need what it offers. (My
&lt;a href="https://durrellgemuh.com/blog/case-study-enterprise-kubernetes-platform/" rel="noopener noreferrer"&gt;Enterprise Kubernetes case study&lt;/a&gt; is where EKS
&lt;em&gt;did&lt;/em&gt; earn its place, 50+ services, GitOps, mesh.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best architecture is the simplest one that meets the requirement. Often that's ECS, and that's&lt;br&gt;
a feature, not a compromise.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ecs</category>
      <category>eks</category>
      <category>fargate</category>
    </item>
    <item>
      <title>How to Become an AWS Community Builder: Complete Guide for 2027 Applications</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Sun, 23 Aug 2026 12:31:52 +0000</pubDate>
      <link>https://dev.to/durrello/how-to-become-an-aws-community-builder-complete-guide-for-2027-applications-2911</link>
      <guid>https://dev.to/durrello/how-to-become-an-aws-community-builder-complete-guide-for-2027-applications-2911</guid>
      <description>&lt;p&gt;The AWS Community Builders program opens applications once a year, typically in early January, and closes within about two weeks. That's a narrow window. If you're serious about the 2027 cycle, you have roughly four months from now to build the contribution track record that gets you selected.&lt;/p&gt;

&lt;p&gt;I wrote &lt;a href="https://durrellgemuh.com/blog/becoming-an-aws-community-builder-from-africa/" rel="noopener noreferrer"&gt;my personal story&lt;/a&gt; about getting into the program from Cameroon. This post is different. It's a practical, no-fluff guide covering how the program works, what the application actually asks, what reviewers evaluate (based on patterns from people who've been accepted and rejected), and how to prepare starting today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the AWS Community Builders Program Actually Is
&lt;/h2&gt;

&lt;p&gt;AWS Community Builders is a global program that recognizes people who share AWS knowledge publicly. Not AWS employees. Not necessarily experts. Engineers, students, content creators, and community organizers who consistently write, build, speak, or contribute to open source around AWS services.&lt;/p&gt;

&lt;p&gt;The key word is &lt;strong&gt;consistently&lt;/strong&gt;. This is not a certification you study for. It's recognition of a public track record of helping others learn and build on AWS.&lt;/p&gt;

&lt;p&gt;The program sits below the AWS Heroes program in AWS's community ladder. Heroes are veterans with years of visible impact. Community Builders is the accessible entry point, and for most engineers reading this, the realistic first target.&lt;/p&gt;

&lt;p&gt;It's free to apply. Membership runs in yearly cycles with renewal based on continued activity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Categories (Pick One That Matches Your Work)
&lt;/h2&gt;

&lt;p&gt;When you apply, you select a technology category. For 2026 the categories were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI Engineering&lt;/strong&gt;: Building generative AI applications with Amazon Bedrock, prompt engineering, RAG, fine-tuning, agents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Operations&lt;/strong&gt;: Observability and configuration (CloudWatch, Systems Manager, Config, Service Catalog)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Containers&lt;/strong&gt;: ECS, EKS, Fargate, App Runner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt;: Databases and analytics (DynamoDB, RDS, S3, OpenSearch, Redshift, Athena)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev Tools&lt;/strong&gt;: CI/CD, CDK, build pipelines, Application Composer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Front-End Web and Mobile&lt;/strong&gt;: API Gateway, Amplify, AppSync&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Machine Learning&lt;/strong&gt;: SageMaker, training and deploying models at scale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Networking and Content Delivery&lt;/strong&gt;: CloudFront, Route 53, WAF, VPC&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Cognito, IAM, GuardDuty, Secrets Manager&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless&lt;/strong&gt;: Lambda, Step Functions, EventBridge, SQS, SNS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick the category where your existing contributions already live, not the one that sounds impressive. If your last six blog posts are about CloudWatch and SSM, apply for Cloud Operations. Reviewers look at your submitted content, and a mismatch between category and contributions is an easy reason to pass on an application.&lt;/p&gt;

&lt;p&gt;I'm in &lt;strong&gt;Cloud Operations&lt;/strong&gt;, which aligns with my infrastructure, observability, and cost-optimization work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AWS Is Actually Evaluating
&lt;/h2&gt;

&lt;p&gt;Through my time in the program and observing who gets accepted, rejected, and eventually succeeds on reapplication, there are clear patterns. AWS is not selecting experts. They're selecting &lt;strong&gt;patterns of behavior&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Patterns of continuous learning&lt;/li&gt;
&lt;li&gt;Patterns of sharing knowledge publicly&lt;/li&gt;
&lt;li&gt;Patterns of consistency over time&lt;/li&gt;
&lt;li&gt;Patterns of helping others in the community&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. Consistency Over Brilliance
&lt;/h3&gt;

&lt;p&gt;One viral post won't do it. Eight decent posts over a year will. The program is literally named for community &lt;em&gt;building&lt;/em&gt;, which is a repeated act.&lt;/p&gt;

&lt;p&gt;The AWS team evaluates your &lt;strong&gt;current behavior and momentum&lt;/strong&gt;, not what you did years ago. They want to see contributions from within the last 12 months. Many applications fail because candidates start contributing too late or only after discovering the program exists.&lt;/p&gt;

&lt;p&gt;If you think "this is too simple" or "this already exists on the internet," you're thinking about it wrong. Every person explains concepts differently. Your perspective, shaped by your real experience, matters more than originality.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Public and Verifiable
&lt;/h3&gt;

&lt;p&gt;Everything you claim needs a URL. Blog with dates. YouTube channel with upload history. GitHub with commit graphs. Meetup talks with event pages. If your work is invisible, it can't count.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Original Voice
&lt;/h3&gt;

&lt;p&gt;AWS wants to hear &lt;em&gt;you&lt;/em&gt;, not polished LLM output. Content that feels synthetic or overly generic doesn't build trust. You can use tools to assist, but if your voice disappears, the signal is lost.&lt;/p&gt;

&lt;p&gt;If your content sounds like documentation, rewrite it. If it sounds like marketing, rewrite it. If it doesn't sound like you, rewrite it.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Genuine Community Engagement
&lt;/h3&gt;

&lt;p&gt;Creating content in isolation isn't enough. Show that you engage with others:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Answering questions on re:Post, Stack Overflow, dev.to&lt;/li&gt;
&lt;li&gt;Responding to comments on your posts&lt;/li&gt;
&lt;li&gt;Helping people in forums and discussions&lt;/li&gt;
&lt;li&gt;Attending or organizing meetups&lt;/li&gt;
&lt;li&gt;Contributing to open-source projects&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Application: Step by Step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Timeline
&lt;/h3&gt;

&lt;p&gt;Applications typically open in the first week of January and close within about two weeks (for 2026 it closed around January 21). Results come in early March. That's why preparation now matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Join the Waitlist
&lt;/h3&gt;

&lt;p&gt;Go to &lt;a href="https://builder.aws.com/community/community-builders" rel="noopener noreferrer"&gt;https://builder.aws.com/community/community-builders&lt;/a&gt; and join the waitlist with a free AWS Builder ID. Do this now. The application form is emailed to the waitlist, and if you're not on it, you find out about the window from other people's LinkedIn posts after it closes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Choose Your Category
&lt;/h3&gt;

&lt;p&gt;Select the one that matches your existing body of work. Don't choose a category you &lt;em&gt;plan&lt;/em&gt; to explore. Choose where you've already been active.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Prepare Your Three Public Links
&lt;/h3&gt;

&lt;p&gt;This is the most important part of the application. You submit 2-3 pieces of public content that demonstrate your contributions. These should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Technically solid&lt;/strong&gt; (accurate, helpful, showing real understanding)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Published at least 3-4 months ago&lt;/strong&gt; (not something rushed the week before applying)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aligned with your chosen category&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publicly accessible&lt;/strong&gt; (no paywalls, no private repos)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Strong examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In-depth technical blog posts or tutorials with code examples&lt;/li&gt;
&lt;li&gt;Video walkthroughs or conference/meetup talk recordings&lt;/li&gt;
&lt;li&gt;Open-source projects or meaningful contributions&lt;/li&gt;
&lt;li&gt;Documentation of events you organized (with photos, recordings, testimonials)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Weak examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A post saying "I passed my certification" with no educational content&lt;/li&gt;
&lt;li&gt;Company blog posts not under your name&lt;/li&gt;
&lt;li&gt;Content behind a paywall&lt;/li&gt;
&lt;li&gt;Links to your LinkedIn profile without specific content&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Write Your Story (About 1000 Characters)
&lt;/h3&gt;

&lt;p&gt;A short written answer on why you want to be part of the community. Write it yourself. Reviewers read hundreds of these, and generated text has a recognizable pattern. Your specific journey, in your own words, beats polished generic text every time.&lt;/p&gt;

&lt;p&gt;AWS wants to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why you're passionate about AWS and cloud&lt;/li&gt;
&lt;li&gt;How you're already helping others&lt;/li&gt;
&lt;li&gt;Your vision for continued contribution&lt;/li&gt;
&lt;li&gt;What makes your perspective unique&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5: Complete Everything
&lt;/h3&gt;

&lt;p&gt;Don't skip any questions, especially about communication preferences. If you don't opt in to emails, AWS can't notify you about your application status even if you're accepted.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Contribute (Starting Now)
&lt;/h2&gt;

&lt;p&gt;If you're reading this in mid-2026, you have roughly 4-5 months before applications open. That's 8-10 biweekly blog posts. Enough. More than enough.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content Ideas That Work
&lt;/h3&gt;

&lt;p&gt;Start with your daily work and learning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configured an AWS service for the first time? Write about what confused you, what failed, and how you fixed it&lt;/li&gt;
&lt;li&gt;Followed an official guide? Explain it again in simpler words with screenshots or diagrams&lt;/li&gt;
&lt;li&gt;Built a proof of concept? Document the architecture, key decisions, and lessons learned&lt;/li&gt;
&lt;li&gt;Hit a production issue? Write the troubleshooting story (sanitized)&lt;/li&gt;
&lt;li&gt;Compared two AWS services? Share your decision framework&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common mistakes, misunderstood services, cost surprises, security misconfigurations, and deployment failures make excellent topics. Even basic content creates impact when it reduces confusion for someone else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Beyond Blog Posts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Short video walkthroughs&lt;/strong&gt; on YouTube or LinkedIn&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answering questions&lt;/strong&gt; on re:Post, Stack Overflow, or community forums (explain &lt;em&gt;why&lt;/em&gt; a solution works, not just what works)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open-source repos&lt;/strong&gt; with sample code, IaC templates, or demo projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organizing or speaking at meetups&lt;/strong&gt; (local AWS user groups, virtual events)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contributing to AWS documentation&lt;/strong&gt; or open-source AWS tools&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Publishing Cadence That Works
&lt;/h3&gt;

&lt;p&gt;Aim for biweekly at minimum. That gives you 8+ pieces before January. The sweet spot seems to be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick one platform and commit to it (blog, dev.to, YouTube, wherever)&lt;/li&gt;
&lt;li&gt;Publish every two weeks, not perfectly, just consistently&lt;/li&gt;
&lt;li&gt;Make sure everything is publicly searchable (not behind auth or paywalls)&lt;/li&gt;
&lt;li&gt;Cross-post to LinkedIn to build visibility&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Common Mistakes That Kill Applications
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Starting too late.&lt;/strong&gt; Discovering the contribution requirement while filling out the application. Start now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focusing only on certifications.&lt;/strong&gt; Certs are valuable, but "I passed the SAA" doesn't teach anyone anything. Focus on educational content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using AI-generated content without your voice.&lt;/strong&gt; Reviewers can tell. Use tools to assist, but the perspective and experience must be yours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistent naming.&lt;/strong&gt; Use the same name across your Builder ID, application, and content. Inconsistency makes verification harder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Applying for the wrong category.&lt;/strong&gt; Your links should match your category. If they don't, that's an easy pass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content behind paywalls or private.&lt;/strong&gt; AWS can't see it if it's not public.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only broadcasting, never engaging.&lt;/strong&gt; Create content AND participate in discussions. One-way communication isn't community building.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What Happens After Selection
&lt;/h2&gt;

&lt;p&gt;Getting the welcome email feels like the finish line. It's the starting line. The first 30 days set the tone for your program year.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claim Benefits Immediately
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;$500-$1000 in AWS credits&lt;/strong&gt;: Redeem in your billing console the day you get access. Credits have expiry dates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Certification voucher&lt;/strong&gt;: 100% coverage for one AWS exam (Foundational, Associate, or Professional/Specialty).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning subscriptions&lt;/strong&gt;: Access to exclusive platforms and content.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Engage in the Slack
&lt;/h3&gt;

&lt;p&gt;The private Slack workspace has channels per category, boost channels where builders amplify each other's content, and announcement channels where challenges and opportunities appear. Lurking gets you nothing. Introduce yourself and keep showing up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep Contributing
&lt;/h3&gt;

&lt;p&gt;Renewal is not automatic. The program reviews activity every cycle. Getting in and going silent is the standard way people fall out after one year. The builders who stay treat selection as the beginning of a publishing habit, not a badge to collect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Watch for Challenges
&lt;/h3&gt;

&lt;p&gt;AWS runs periodic content challenges for builders. Winners get visibility with the AWS teams in your category, and prizes. These are worth participating in.&lt;/p&gt;

&lt;h2&gt;
  
  
  If You Get Rejected
&lt;/h2&gt;

&lt;p&gt;This is normal. Many successful Community Builders were rejected on their first attempt and selected the next year. The pattern is consistent across the community.&lt;/p&gt;

&lt;p&gt;Rejection is feedback, even when it's silent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your contributions may have started too recently&lt;/li&gt;
&lt;li&gt;Your content may not have been deep enough&lt;/li&gt;
&lt;li&gt;Your links may not have matched your chosen category&lt;/li&gt;
&lt;li&gt;You may simply need more visible community engagement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application costs nothing but the form. The real application is the 12 months of public content before it. Use a rejection as fuel to contribute more, then reapply.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Honest Take on the Program's Value
&lt;/h2&gt;

&lt;p&gt;What it does well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;credits and voucher&lt;/strong&gt; have direct, countable value (around $800+ combined if you use both)&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Slack access&lt;/strong&gt; is a real network you cannot buy: direct lines to AWS service teams, collaboration with builders worldwide&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;external recognition&lt;/strong&gt; opens doors, especially for engineers building international careers from regions without a big local AWS presence&lt;/li&gt;
&lt;li&gt;It creates a &lt;strong&gt;forcing function&lt;/strong&gt; to keep contributing publicly, which compounds over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it won't do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It won't make you write. There's no content quota enforced month to month.&lt;/li&gt;
&lt;li&gt;It won't hand you clients or a salary bump directly.&lt;/li&gt;
&lt;li&gt;It won't substitute for deep technical skills.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The program amplifies whatever habits you already have. If your habit is building in public, it multiplies you. If your habit is silence, you'll be silent with a badge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Timeline from Here
&lt;/h2&gt;

&lt;p&gt;If you're reading this in August 2026 and targeting the January 2027 application:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;What to Do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Now&lt;/td&gt;
&lt;td&gt;Join the waitlist at builder.aws.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aug-Sep&lt;/td&gt;
&lt;td&gt;Publish 2-3 solid AWS posts in your target category&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct-Nov&lt;/td&gt;
&lt;td&gt;Continue publishing biweekly, engage in communities, answer questions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dec&lt;/td&gt;
&lt;td&gt;Prepare your 3 strongest links, draft your story, review everything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jan 2027&lt;/td&gt;
&lt;td&gt;Submit application within the window (it's only open ~2 weeks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mar 2027&lt;/td&gt;
&lt;td&gt;Results arrive&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Start now. The best time to have started was six months ago. The second best time is today.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>career</category>
      <category>community</category>
      <category>cloud</category>
    </item>
    <item>
      <title>AWS Cost Optimization: 15 Strategies That Work</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Sun, 16 Aug 2026 12:17:28 +0000</pubDate>
      <link>https://dev.to/durrello/aws-cost-optimization-15-strategies-that-work-3hl3</link>
      <guid>https://dev.to/durrello/aws-cost-optimization-15-strategies-that-work-3hl3</guid>
      <description>&lt;p&gt;Cloud costs can spiral out of control faster than most engineering teams realize. I've helped multiple organizations reduce their AWS bills by 40-70% through systematic cost optimization. These aren't theoretical savings, these are real numbers from production environments.&lt;/p&gt;

&lt;p&gt;This post covers the most effective strategies I've implemented, with specific examples and implementation steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding AWS Cost Drivers
&lt;/h2&gt;

&lt;p&gt;Before optimizing, you need visibility. The biggest cost drivers in most AWS environments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;EC2 Instances&lt;/strong&gt; (35-50% of total spend)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EBS Storage&lt;/strong&gt; (15-25%)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Transfer&lt;/strong&gt; (10-20%)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RDS Databases&lt;/strong&gt; (10-15%)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda &amp;amp; Serverless&lt;/strong&gt; (5-10%)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Strategy 1: Right-Size Your EC2 Instances
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: 20-40% savings&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 2-4 weeks&lt;/p&gt;

&lt;h3&gt;
  
  
  The Process
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enable CloudWatch Detailed Monitoring&lt;/strong&gt; on all instances&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use AWS Compute Optimizer&lt;/strong&gt; for recommendations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analyze usage patterns&lt;/strong&gt; over 2-week periods&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement changes&lt;/strong&gt; during maintenance windows&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Real Example
&lt;/h3&gt;

&lt;p&gt;A client had 20 m5.large instances running at 15% CPU average. We resized to m5a.medium, saving $8,400/month while maintaining performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AWS Compute Optimizer&lt;/li&gt;
&lt;li&gt;CloudWatch metrics&lt;/li&gt;
&lt;li&gt;EC2 Instance Selector (CLI tool)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Strategy 2: Implement Auto Scaling
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: 30-50% savings on variable workloads&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 1-2 weeks&lt;/p&gt;

&lt;h3&gt;
  
  
  When It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Web applications with variable traffic&lt;/li&gt;
&lt;li&gt;Batch processing workloads&lt;/li&gt;
&lt;li&gt;Development environments&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# CloudFormation template snippet&lt;/span&gt;
&lt;span class="na"&gt;AutoScalingGroup&lt;/span&gt;&lt;span class="pi"&gt;:&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;10'&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;3'&lt;/span&gt;
  &lt;span class="na"&gt;MetricsCollection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Granularity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1Minute'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Real Result
&lt;/h3&gt;

&lt;p&gt;A client's API service scaled from 3 to 12 instances during peak hours, then back down. Monthly savings: $15,000.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 3: Use Spot Instances for Non-Critical Workloads
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: 60-80% savings vs on-demand&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 1 week&lt;/p&gt;

&lt;h3&gt;
  
  
  Suitable Workloads
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;CI/CD pipelines&lt;/li&gt;
&lt;li&gt;Batch processing&lt;/li&gt;
&lt;li&gt;Development environments&lt;/li&gt;
&lt;li&gt;Non-production workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation Considerations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use Spot Instance Advisor for interruption rates&lt;/li&gt;
&lt;li&gt;Implement proper fallback to on-demand&lt;/li&gt;
&lt;li&gt;Test interruption handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Real Example
&lt;/h3&gt;

&lt;p&gt;A client's ML training pipeline moved to spot instances, reducing costs from $50,000/month to $12,000/month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 4: Optimize EBS Storage
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: 15-30% savings&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 2-3 weeks&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Tactics
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use gp3 instead of gp2&lt;/strong&gt; (20% cheaper, better performance)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implement EBS snapshots lifecycle policies&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use instance store for temporary data&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Right-size EBS volumes&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Real Savings
&lt;/h3&gt;

&lt;p&gt;Client migrated 200 gp2 volumes to gp3: $3,200/month savings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 5: Database Optimization
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: 25-40% savings&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 3-6 weeks&lt;/p&gt;

&lt;h3&gt;
  
  
  RDS Optimization Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Right-size instances&lt;/strong&gt; based on CPU/memory usage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use reserved instances&lt;/strong&gt; for predictable workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement read replicas&lt;/strong&gt; for read-heavy workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize storage&lt;/strong&gt; (gp3, proper IOPS allocation)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Real Example
&lt;/h3&gt;

&lt;p&gt;A client's PostgreSQL database was over-provisioned. We:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduced instance size from r5.xlarge to r5.large&lt;/li&gt;
&lt;li&gt;Added read replica for reporting queries&lt;/li&gt;
&lt;li&gt;Monthly savings: $4,500&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Strategy 6: Implement Savings Plans
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: 20-40% savings on compute&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 1-2 weeks&lt;/p&gt;

&lt;h3&gt;
  
  
  Types of Savings Plans
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compute Savings Plans&lt;/strong&gt;: Most flexible, 1-3 year commitment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EC2 Instance Savings Plans&lt;/strong&gt;: Instance-family specific&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SageMaker Savings Plans&lt;/strong&gt;: For ML workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Real Result
&lt;/h3&gt;

&lt;p&gt;Client committed to 1-year Compute Savings Plan covering 70% of usage: $25,000/month savings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 7: Optimize Data Transfer Costs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: 10-25% savings&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 2-4 weeks&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost Reduction Tactics
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use CloudFront&lt;/strong&gt; for content delivery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compress data&lt;/strong&gt; before transfer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use VPC endpoints&lt;/strong&gt; instead of NAT gateways&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimize cross-region transfers&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Real Example
&lt;/h3&gt;

&lt;p&gt;Client implemented CloudFront for static assets: $8,000/month data transfer savings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 8: Lambda Cost Optimization
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: 20-50% savings&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 1-2 weeks&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimization Strategies
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Right-size memory allocation&lt;/strong&gt; (affects CPU proportionally)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize function duration&lt;/strong&gt; through code improvements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use provisioned concurrency&lt;/strong&gt; for latency-sensitive functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement proper error handling&lt;/strong&gt; to avoid retries&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Real Example
&lt;/h3&gt;

&lt;p&gt;A client's Lambda functions were over-allocated. Optimization reduced costs from $12,000/month to $4,800/month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 9: Implement Resource Tagging Strategy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: Improved governance, enables cost allocation&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 2-4 weeks&lt;/p&gt;

&lt;h3&gt;
  
  
  Tagging Strategy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Environment&lt;/strong&gt;: prod, staging, dev&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team&lt;/strong&gt;: engineering, marketing, data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project&lt;/strong&gt;: specific project names&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost-Center&lt;/strong&gt;: department codes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;Use AWS Config Rules to enforce tagging compliance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 10: Monitor and Alert on Cost Anomalies
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: Prevents cost overruns&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 1 week&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Cost Anomaly Detection
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Set up automated alerts&lt;/li&gt;
&lt;li&gt;Configure alerts by service, account, or tag&lt;/li&gt;
&lt;li&gt;Integrate with Slack/PagerDuty&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Strategy 11: Clean Up Unused Resources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: 5-15% savings&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 1-2 weeks&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Unused Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;EBS volumes not attached to instances&lt;/li&gt;
&lt;li&gt;Elastic IPs not associated&lt;/li&gt;
&lt;li&gt;NAT gateways in unused subnets&lt;/li&gt;
&lt;li&gt;Unused load balancers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Automation
&lt;/h3&gt;

&lt;p&gt;Use AWS Config and Lambda for automated cleanup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 12: Optimize Container Orchestration
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: 20-35% savings&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 2-4 weeks&lt;/p&gt;

&lt;h3&gt;
  
  
  EKS Optimization
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use Fargate&lt;/strong&gt; for variable workloads&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implement cluster autoscaling&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use spot instances&lt;/strong&gt; for worker nodes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimize pod resource requests/limits&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Strategy 13: Implement Backup and Disaster Recovery Optimization
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: 15-25% savings&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 2-3 weeks&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategies
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use cross-region replication&lt;/strong&gt; strategically&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implement backup lifecycle policies&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use cheaper storage classes&lt;/strong&gt; for older backups&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test and clean up&lt;/strong&gt; unused AMIs and snapshots&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Strategy 14: Use AWS Organizations for Consolidated Billing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: Volume discounts, better negotiation leverage&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: 1-2 weeks&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Combined usage for better pricing tiers&lt;/li&gt;
&lt;li&gt;Centralized cost management&lt;/li&gt;
&lt;li&gt;Easier resource sharing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Strategy 15: Implement FinOps Culture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: 30-50% long-term savings&lt;br&gt;
&lt;strong&gt;Implementation Time&lt;/strong&gt;: Ongoing&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Practices
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cost awareness training&lt;/strong&gt; for engineering teams&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regular cost reviews&lt;/strong&gt; and optimization sessions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost budgets&lt;/strong&gt; and alerts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure as Code&lt;/strong&gt; for cost predictability&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tools and Services for Cost Optimization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AWS Native Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cost Explorer&lt;/li&gt;
&lt;li&gt;Budgets&lt;/li&gt;
&lt;li&gt;Cost and Usage Reports&lt;/li&gt;
&lt;li&gt;Trusted Advisor&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Third-Party Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;CloudHealth&lt;/li&gt;
&lt;li&gt;Cloudability&lt;/li&gt;
&lt;li&gt;CloudCheckr&lt;/li&gt;
&lt;li&gt;Spot.io&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation Roadmap
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Phase 1 (Quick Wins - 1-2 weeks)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Enable detailed monitoring&lt;/li&gt;
&lt;li&gt;Set up cost alerts&lt;/li&gt;
&lt;li&gt;Clean up obvious unused resources&lt;/li&gt;
&lt;li&gt;Implement basic auto scaling&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Phase 2 (Medium Impact - 2-4 weeks)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Right-size instances and storage&lt;/li&gt;
&lt;li&gt;Implement Savings Plans&lt;/li&gt;
&lt;li&gt;Optimize databases&lt;/li&gt;
&lt;li&gt;Set up proper tagging&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Phase 3 (Advanced Optimization - 1-3 months)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Implement advanced auto scaling&lt;/li&gt;
&lt;li&gt;Use spot instances strategically&lt;/li&gt;
&lt;li&gt;Optimize data transfer&lt;/li&gt;
&lt;li&gt;Implement FinOps processes&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Measuring Success
&lt;/h2&gt;

&lt;p&gt;Track these metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cost per user/transaction&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost optimization percentage&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reserved instance utilization&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource utilization rates&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Pitfalls to Avoid
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Optimizing too early&lt;/strong&gt;: Focus on business value first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Over-optimization&lt;/strong&gt;: Don't sacrifice reliability for cost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignoring operational overhead&lt;/strong&gt;: Complex optimizations need maintenance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not monitoring after changes&lt;/strong&gt;: Always validate that optimizations work&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;AWS cost optimization is an ongoing process, not a one-time project. The strategies above have consistently delivered 40-70% savings for my clients, but the key is systematic implementation and continuous monitoring.&lt;/p&gt;

&lt;p&gt;Start with visibility, implement quick wins first, then tackle more complex optimizations. Remember: efficient infrastructure enables faster feature delivery and better user experience.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>infrastructure</category>
      <category>optimization</category>
    </item>
    <item>
      <title>AWS IAM Least Privilege Without Losing Your Mind</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Sun, 16 Aug 2026 11:36:43 +0000</pubDate>
      <link>https://dev.to/durrello/aws-iam-least-privilege-without-losing-your-mind-3n1j</link>
      <guid>https://dev.to/durrello/aws-iam-least-privilege-without-losing-your-mind-3n1j</guid>
      <description>&lt;p&gt;Least privilege is the IAM principle everyone agrees with and few actually implement, because doing&lt;br&gt;
it by hand is tedious and &lt;code&gt;"Action": "*"&lt;/code&gt; always works. Here's a practical approach that gets you&lt;br&gt;
most of the way without turning into a full-time job.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;An over-permissive role is a standing risk: if the credentials leak or a service is compromised, the&lt;br&gt;
blast radius is everything that role can touch. Least privilege shrinks that blast radius to "only&lt;br&gt;
what this thing actually needs."&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Start from deny, add what's used
&lt;/h2&gt;

&lt;p&gt;Don't start from a broad policy and try to trim it, start from nothing and add what the workload&lt;br&gt;
actually calls. The trick is knowing what it calls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CloudTrail + IAM Access Analyzer&lt;/strong&gt; can generate a policy from observed activity. Run the workload
with a broad policy in a non-prod account, let it exercise its paths, then generate a scoped policy
from what it actually used.&lt;/li&gt;
&lt;li&gt;For new workloads, add permissions iteratively as &lt;code&gt;AccessDenied&lt;/code&gt; errors appear, annoying for a
day, correct forever.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  2. Scope resources, not just actions
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;s3:GetObject&lt;/code&gt; on &lt;code&gt;"Resource": "*"&lt;/code&gt; means &lt;em&gt;every&lt;/em&gt; object in &lt;em&gt;every&lt;/em&gt; bucket. Scope it:&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;"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;"Action"&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="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:::my-app-bucket/*"&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;Most managed policies are convenient but broad. Prefer customer-managed policies scoped to your&lt;br&gt;
actual ARNs.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Use conditions to tighten further
&lt;/h2&gt;

&lt;p&gt;Conditions are the underused superpower:&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="nl"&gt;"Condition"&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;"StringEquals"&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;"aws:RequestedRegion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&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;"Bool"&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;"aws:SecureTransport"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&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;Restrict by region, source VPC, MFA presence, tag, or time. A policy that only works from your VPC&lt;br&gt;
over TLS is far safer than one that works from anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Prefer roles over long-lived keys
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Workloads on AWS: use &lt;strong&gt;IAM roles&lt;/strong&gt; (instance profiles, IRSA for EKS, task roles for ECS), no
stored keys.&lt;/li&gt;
&lt;li&gt;CI/CD and external systems: use &lt;strong&gt;OIDC federation&lt;/strong&gt; so there are no long-lived &lt;code&gt;AKIA…&lt;/code&gt; keys to
leak.&lt;/li&gt;
&lt;li&gt;Humans: use &lt;strong&gt;IAM Identity Center (SSO)&lt;/strong&gt; with short-lived sessions, not IAM users.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Guardrails above the role: SCPs
&lt;/h2&gt;

&lt;p&gt;Even a perfect role can't protect you from a thousand imperfect ones. &lt;strong&gt;Service Control Policies&lt;/strong&gt; at&lt;br&gt;
the AWS Organizations level set hard ceilings e.g. "deny disabling CloudTrail," "deny outside&lt;br&gt;
approved regions", that no account or role can exceed. (My&lt;br&gt;
&lt;a href="https://github.com/durrello/aws-multi-account-organization" rel="noopener noreferrer"&gt;aws-multi-account-organization&lt;/a&gt; repo&lt;br&gt;
shows this pattern.)&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Find and fix what's already over-permissioned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAM Access Analyzer&lt;/strong&gt; flags resources shared externally and unused access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Last accessed&lt;/strong&gt; data (in the IAM console) shows permissions a role hasn't used in months, 
strong candidates to remove.&lt;/li&gt;
&lt;li&gt;Alert on new &lt;code&gt;iam:*&lt;/code&gt; and &lt;code&gt;*:*&lt;/code&gt; policies in CI with tfsec/checkov.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The wildcard traps to avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"Action": "*"&lt;/code&gt; on &lt;code&gt;"Resource": "*"&lt;/code&gt;, admin in disguise.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iam:PassRole&lt;/code&gt; with &lt;code&gt;"Resource": "*"&lt;/code&gt;, lets a principal hand any role to a service (privilege
escalation).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NotAction&lt;/code&gt; policies, easy to reason about wrong; prefer explicit &lt;code&gt;Action&lt;/code&gt; allow-lists.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A workable routine
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;New workload → start denied, generate a policy from observed use.&lt;/li&gt;
&lt;li&gt;Scope every statement to specific ARNs.&lt;/li&gt;
&lt;li&gt;Add region/transport/tag conditions where they fit.&lt;/li&gt;
&lt;li&gt;No long-lived keys, roles and OIDC only.&lt;/li&gt;
&lt;li&gt;SCPs as the org-wide ceiling.&lt;/li&gt;
&lt;li&gt;Quarterly: prune unused permissions via Access Analyzer + last-accessed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Least privilege isn't a one-time project; it's a habit backed by the right tools. Done this way, it's maintainable, not the soul-crushing exercise it's reputed to be.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>iam</category>
      <category>security</category>
      <category>cloud</category>
    </item>
    <item>
      <title>The Reality of Working as a DevOps Engineer in Cameroon</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Fri, 14 Aug 2026 22:04:43 +0000</pubDate>
      <link>https://dev.to/durrello/the-reality-of-working-as-a-devops-engineer-in-cameroon-1k7l</link>
      <guid>https://dev.to/durrello/the-reality-of-working-as-a-devops-engineer-in-cameroon-1k7l</guid>
      <description>&lt;p&gt;Most "how to become a DevOps engineer" posts are written from places where electricity and internet&lt;br&gt;
are background assumptions. Mine isn't. I build and operate cloud infrastructure for teams across&lt;br&gt;
six countries, from Buea, Cameroon. It works, and it's a genuinely good career. But it's not the&lt;br&gt;
frictionless picture the highlight reels show.&lt;/p&gt;

&lt;p&gt;Here's the honest version: what's hard, what I do about it, and why it's still worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The power situation
&lt;/h2&gt;

&lt;p&gt;Electricity is the first real constraint. Load shedding and unplanned outages are part of life in&lt;br&gt;
Buea and much of Cameroon. When you're mid-deploy or on an incident call, the lights going out isn't&lt;br&gt;
an inconvenience, it's a work-stopping event.&lt;/p&gt;

&lt;p&gt;What actually helps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A laptop, not a desktop.&lt;/strong&gt; Sounds obvious, but a desktop tower ties your work to mains power. A
laptop gives you hours of buffer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A UPS and/or a power bank/inverter&lt;/strong&gt; sized for your router too: a charged laptop is useless if
the internet box is dead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A backup power plan&lt;/strong&gt; for long outages: a small generator, a solar setup, or knowing which café
or co-working spot has power.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Designing work to be resumable.&lt;/strong&gt; I lean heavily on Git, infrastructure-as-code, and CI so that
if I drop offline mid-task, nothing is lost and a pipeline can finish what I started.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DevOps is actually well-suited to this: the heavy lifting runs in the cloud, not on my machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Internet: the real bottleneck
&lt;/h2&gt;

&lt;p&gt;Connectivity has improved a lot, but it's still the thing most likely to bite you. Bandwidth is&lt;br&gt;
variable, latency to US/EU regions is high, and a fibre cut can mean a bad day. For work that&lt;br&gt;
involves pushing container images, pulling large dependencies, or screen-sharing during incidents,&lt;br&gt;
that matters.&lt;/p&gt;

&lt;p&gt;How I work around it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A primary connection plus a mobile-data backup&lt;/strong&gt; (different carrier). When one drops, I tether.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do heavy lifting in the cloud.&lt;/strong&gt; I run builds, image pushes, and big &lt;code&gt;terraform apply&lt;/code&gt;s from CI
or a cloud VM/bastion close to the target region, not over my home link. My laptop just drives
it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache aggressively.&lt;/strong&gt; Local Docker layer caching, package mirrors, and &lt;code&gt;terraform plan&lt;/code&gt; review
before apply cut the amount I transfer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async-first communication.&lt;/strong&gt; I don't rely on a perfect video call to get work done; clear
written updates survive a flaky connection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The mindset shift: treat your local connection as unreliable by design, and architect around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timezones: a hidden advantage, with discipline
&lt;/h2&gt;

&lt;p&gt;Cameroon is on WAT (UTC+1). Working with US teams means they're 6-9 hours behind; with parts of Asia&lt;br&gt;
I'm behind. That overlap gap is real, but it's also a feature if you're deliberate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The early morning is golden.&lt;/strong&gt; Before the US wakes up, I get deep, uninterrupted work done: the
best focus hours of my day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overlap windows are for humans, async is for everything else.&lt;/strong&gt; I protect the few overlap hours
for the conversations that genuinely need to be live, and push status, reviews, and decisions to
writing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On-call across timezones needs honesty.&lt;/strong&gt; If a team needs 3am-my-time coverage, that has to be
an explicit, compensated agreement, not an silent expectation. I've learned to negotiate this up
front.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Being in a different timezone has won me work: I can cover hours the rest of the team can't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Payments and the business side
&lt;/h2&gt;

&lt;p&gt;This is the part nobody warns you about. Getting paid from abroad is harder than the work itself&lt;br&gt;
some months:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not every international platform supports Cameroon cleanly; some payment processors and payout
options just aren't available.&lt;/li&gt;
&lt;li&gt;I've had to get creative with a mix of services to receive funds reliably, and factor in fees and
exchange rates.&lt;/li&gt;
&lt;li&gt;Invoicing, contracts, and proving legitimacy as a solo engineer from Cameroon takes extra effort,
a professional website, a real GitHub, and references do a lot of the trust-building that a
recognisable local employer would otherwise provide.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of it is a blocker. It's friction you plan for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visibility: you have to be undeniable
&lt;/h2&gt;

&lt;p&gt;When you're not in a hub city and don't have a FAANG logo on your CV, the burden of proof is higher.&lt;br&gt;
A hiring manager scrolling past you needs an immediate reason to stop. For me that's been:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Public, runnable projects&lt;/strong&gt; that show I operate real systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writing&lt;/strong&gt; (like this) that shows how I think.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community work&lt;/strong&gt;: AWS Community Builder, and running Talent Forge to train
other engineers here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can't control the bias; you can make yourself impossible to dismiss.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's still absolutely worth it
&lt;/h2&gt;

&lt;p&gt;For all of the above, I wouldn't trade it. I do interesting, well-paid, global-standard engineering&lt;br&gt;
without leaving home. I get to build the local DevOps community I wish I'd had. And the constraints&lt;br&gt;
made me a better engineer, when your power and internet can vanish, you build systems that are&lt;br&gt;
resilient, automated, and observable by default. That's literally the job.&lt;/p&gt;

&lt;p&gt;If you're an engineer in Cameroon, or anywhere with these same constraints, wondering whether a&lt;br&gt;
remote DevOps career is realistic: it is. Plan for the friction, make your work undeniable, and ship.&lt;/p&gt;




&lt;p&gt;*I'm a DevOps &amp;amp; Cloud Engineer based in Buea, Cameroon, working with teams worldwide and open to new opportunities.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>career</category>
      <category>remote</category>
      <category>africa</category>
    </item>
    <item>
      <title>Building LogSentry: A Serverless AWS Pipeline That Detects Secrets Leaked into Application Logs in Real-Time</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Mon, 08 Jun 2026 02:02:20 +0000</pubDate>
      <link>https://dev.to/durrello/building-logsentry-detecting-secrets-leaked-into-application-logs-4k5o</link>
      <guid>https://dev.to/durrello/building-logsentry-detecting-secrets-leaked-into-application-logs-4k5o</guid>
      <description>&lt;p&gt;A serverless AWS pipeline that auto-monitors every CloudWatch log group, scans for leaked credentials in real-time, sends rate-limited alerts, and ships with a triage dashboard — zero config after deploy.&lt;/p&gt;

&lt;p&gt;Every production system I've operated has had the same recurring incident: a developer accidentally logs a password, an API key ends up in CloudWatch, a database connection string appears in a debug statement that was never removed.&lt;/p&gt;

&lt;p&gt;The consequences are serious. A leaked AWS key in logs can be scraped by attackers in minutes. A database URL with credentials gives direct access. A Stripe key means money.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;LogSentry&lt;/strong&gt; to catch these leaks the moment they happen — before they become breaches. And critically, it requires &lt;strong&gt;zero configuration&lt;/strong&gt; after the initial deploy. New services are monitored automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;In a typical microservices environment running 20+ services, each generating thousands of log lines per minute, manual review is impossible. Teams discover leaked secrets in one of three ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A security audit (weeks or months later)&lt;/li&gt;
&lt;li&gt;An incident (the secret was already exploited)&lt;/li&gt;
&lt;li&gt;A colleague happens to notice during debugging&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We need detection that's real-time, automated, comprehensive, and low-noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Services → CloudWatch → [Auto-Subscribe] → Kinesis → Lambda Scanner → DynamoDB + SNS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Services generate logs&lt;/strong&gt; → CloudWatch Log Groups&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-subscribe&lt;/strong&gt; (EventBridge + 5-min scheduled scan) adds subscription filters to every log group — zero manual setup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kinesis Data Stream&lt;/strong&gt; buffers log events (backpressure, replay, scaling)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Scanner&lt;/strong&gt; processes events: 12 regex patterns + Shannon entropy analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB&lt;/strong&gt; stores deduplicated findings (with TTL auto-expiry at 90 days)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SNS&lt;/strong&gt; sends rate-limited alerts to Slack/email/PagerDuty&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch custom metrics&lt;/strong&gt; track detection rates per severity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQS Dead Letter Queue&lt;/strong&gt; preserves failed events for 14 days&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Zero-Config Auto-Subscribe
&lt;/h2&gt;

&lt;p&gt;The key differentiator is &lt;strong&gt;automatic monitoring&lt;/strong&gt;. Instead of manually attaching subscription filters to each log group, LogSentry handles it:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;New log group created&lt;/td&gt;
&lt;td&gt;Instant&lt;/td&gt;
&lt;td&gt;EventBridge rule (requires CloudTrail)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduled fallback&lt;/td&gt;
&lt;td&gt;≤ 5 minutes&lt;/td&gt;
&lt;td&gt;Lambda scans for unsubscribed groups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First deploy&lt;/td&gt;
&lt;td&gt;Immediate&lt;/td&gt;
&lt;td&gt;Terraform local-exec subscribes existing groups&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Excluded prefixes (configurable): &lt;code&gt;/aws/lambda/logsentry&lt;/code&gt;, &lt;code&gt;/aws/cloudtrail&lt;/code&gt;, &lt;code&gt;/aws/rds&lt;/code&gt; — to avoid scanning its own logs or known-noisy sources.&lt;/p&gt;

&lt;p&gt;After &lt;code&gt;terraform apply&lt;/code&gt;, every existing and future log group is monitored. Deploy a new service tomorrow — it's covered automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Detection Engine
&lt;/h2&gt;

&lt;p&gt;The scanner applies 12 patterns covering the most common secret types:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;What It Catches&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS Access Key&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AKIA&lt;/code&gt; + 16 chars&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password in logs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;password=&lt;/code&gt;, &lt;code&gt;pwd:&lt;/code&gt; with value&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;postgres://user:pass@host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private Key&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-----BEGIN RSA PRIVATE KEY-----&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stripe Key&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sk_live_&lt;/code&gt;, &lt;code&gt;pk_live_&lt;/code&gt; prefixes&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Token&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ghp_&lt;/code&gt;, &lt;code&gt;gho_&lt;/code&gt;, &lt;code&gt;ghs_&lt;/code&gt; prefixes&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JWT Token&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;eyJ&lt;/code&gt; base64 pattern&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bearer Token&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Bearer&lt;/code&gt; + 20+ chars&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slack Token&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;xoxb-&lt;/code&gt;, &lt;code&gt;xoxp-&lt;/code&gt; patterns&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generic API Key&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;api_key=&lt;/code&gt;, &lt;code&gt;api_secret=&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generic Secret&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;secret=&lt;/code&gt;, &lt;code&gt;token=&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Reducing False Positives with Entropy
&lt;/h3&gt;

&lt;p&gt;Raw regex matching produces too much noise. The string &lt;code&gt;password=loading&lt;/code&gt; matches the pattern but isn't a credential.&lt;/p&gt;

&lt;p&gt;LogSentry uses &lt;strong&gt;Shannon entropy analysis&lt;/strong&gt; as a secondary filter. Real secrets have high randomness (entropy &amp;gt; 3.5 bits/char). Dictionary words and common values have low entropy and get filtered.&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_entropy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="n"&gt;entropy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;p_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;entropy&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;p_x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;entropy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This noticeably cuts false positives on generic patterns like &lt;code&gt;secret=&lt;/code&gt; and &lt;code&gt;token=&lt;/code&gt;, where the value is what tells a real credential apart from a placeholder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate-Limited Alerting
&lt;/h2&gt;

&lt;p&gt;A burst of 1,000 identical leaked secrets shouldn't generate 1,000 alerts. LogSentry handles this at two levels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deduplication&lt;/strong&gt; — DynamoDB conditional writes (&lt;code&gt;attribute_not_exists(finding_id)&lt;/code&gt;). Same secret = same finding ID = one alert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting&lt;/strong&gt; — Maximum 10 alerts per Lambda invocation (configurable). Prevents notification storms during mass-logging incidents.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When a new finding is detected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚨 LogSentry Alert — CRITICAL

Pattern: AWS Access Key ID detected
Service: /app/payment-service
Value: AKIA************MPLE
Time: 2026-06-08T14:23:01Z
Environment: production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Dashboard
&lt;/h2&gt;

&lt;p&gt;I built a Flask dashboard that works in two modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Demo mode&lt;/strong&gt; (&lt;code&gt;python app.py&lt;/code&gt;) — uses sample data, no AWS credentials needed. Perfect for showcasing to a team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live mode&lt;/strong&gt; (&lt;code&gt;LOGSENTRY_MODE=live python app.py&lt;/code&gt;) — reads directly from DynamoDB.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dashboard features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stats overview&lt;/strong&gt; — total findings, broken down by severity and status&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Findings list&lt;/strong&gt; — filter by severity, status, service, pattern&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live scanner&lt;/strong&gt; — paste any log line and instantly see what patterns match&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resolve workflow&lt;/strong&gt; — one-click to mark findings as remediated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service breakdown&lt;/strong&gt; — which services are leaking the most&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The live scanner is particularly useful for developers: paste a log line from your app and verify it doesn't contain detectable secrets before deploying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto-Expiring Findings
&lt;/h2&gt;

&lt;p&gt;Resolved findings don't need to live forever. DynamoDB TTL automatically deletes resolved items after 90 days (configurable). This keeps the table clean and costs low without manual maintenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure as Code
&lt;/h2&gt;

&lt;p&gt;Everything is provisioned with a single &lt;code&gt;terraform apply&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kinesis Data Stream (1 shard dev, 2 shards prod)&lt;/li&gt;
&lt;li&gt;Scanner Lambda (zip deploy, no Docker needed)&lt;/li&gt;
&lt;li&gt;Auto-subscribe Lambda + EventBridge rules + scheduled trigger&lt;/li&gt;
&lt;li&gt;DynamoDB table (GSI on severity, TTL, point-in-time recovery)&lt;/li&gt;
&lt;li&gt;SNS topic with email subscription&lt;/li&gt;
&lt;li&gt;SQS Dead Letter Queue&lt;/li&gt;
&lt;li&gt;CloudWatch alarms (error rate, critical findings)&lt;/li&gt;
&lt;li&gt;IAM roles (least-privilege, scoped per function)&lt;/li&gt;
&lt;li&gt;Subscription filters on all existing log groups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No manual steps. No clicking in the console.&lt;/p&gt;

&lt;h2&gt;
  
  
  CI/CD Pipeline
&lt;/h2&gt;

&lt;p&gt;Push-to-deploy via GitHub Actions (with GitLab CI equivalent):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Push → pytest (20 tests) → Trivy scan → Zip + deploy Lambda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Terraform infrastructure has its own pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Push → terraform fmt → validate → tfsec → plan → apply (manual gate for prod)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lambda is deployed as a zip file — no Docker build step, no ECR, no image management. Keeps CI fast (under 2 minutes).&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;20 unit tests covering detection of each pattern type, false positive rejection, multiple findings in one line, entropy calculation, secret masking, deduplication logic, and finding structure validation.&lt;/p&gt;

&lt;p&gt;All tests run in under a second with zero AWS dependencies — the scanner logic is pure Python with lazy-loaded boto3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero-config monitoring is the goal&lt;/strong&gt; — if you require manual steps per service, coverage will always have gaps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deduplication + rate limiting are essential&lt;/strong&gt; — without them, alert fatigue kills the system&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entropy filtering works&lt;/strong&gt; — Shannon entropy is a simple, effective way to cut false positives on generic patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless fits perfectly&lt;/strong&gt; — bursty workload, scales with log volume, near-zero cost at quiet times&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A dashboard makes it real&lt;/strong&gt; — having a live scanner and resolve workflow turns this from a notification tool into a security platform&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The full source is on GitHub: &lt;a href="https://github.com/durrello/logsentry" rel="noopener noreferrer"&gt;github.com/durrello/logsentry&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Security built into the platform, not bolted on after an incident. If your services are generating logs without automated secret detection, you're relying on luck.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>serverless</category>
      <category>devops</category>
    </item>
    <item>
      <title>Build a Portable Development Playground with Vagrant and VirtualBox</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Mon, 01 Jun 2026 17:33:21 +0000</pubDate>
      <link>https://dev.to/durrello/build-a-portable-development-playground-with-vagrant-and-virtualbox-3p90</link>
      <guid>https://dev.to/durrello/build-a-portable-development-playground-with-vagrant-and-virtualbox-3p90</guid>
      <description>&lt;p&gt;&lt;em&gt;Stop saying "it works on my machine." Start shipping consistent environments.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Every developer has been there: you clone a repo, follow the README, and two hours later you're deep in dependency hell — wrong Python version, missing system library, or some obscure config that only works on macOS Ventura and above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vagrant&lt;/strong&gt; paired with &lt;strong&gt;VirtualBox&lt;/strong&gt; solves this elegantly. You define your environment as code, and anyone on your team — Windows, macOS, or Linux — spins up an &lt;em&gt;identical&lt;/em&gt; VM in minutes.&lt;/p&gt;

&lt;p&gt;In this guide, you'll go from zero to a fully reproducible development playground, complete with provisioning, shared folders, and port forwarding.&lt;/p&gt;

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

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

&lt;ul&gt;
&lt;li&gt;A machine with at least &lt;strong&gt;8 GB RAM&lt;/strong&gt; (4 GB for the VM + your host OS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VirtualBox&lt;/strong&gt; installed → &lt;a href="https://www.virtualbox.org/wiki/Downloads" rel="noopener noreferrer"&gt;virtualbox.org/wiki/Downloads&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vagrant&lt;/strong&gt; installed → &lt;a href="https://developer.hashicorp.com/vagrant/downloads" rel="noopener noreferrer"&gt;developer.hashicorp.com/vagrant/downloads&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify your installs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vagrant &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# Vagrant 2.4.x&lt;/span&gt;

VBoxManage &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# 7.x.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1 — Initialize Your Project
&lt;/h2&gt;

&lt;p&gt;Create a new directory for your playground and initialize Vagrant inside it:&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;mkdir &lt;/span&gt;my-dev-playground &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;my-dev-playground
vagrant init ubuntu/jammy64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates a &lt;code&gt;Vagrantfile&lt;/code&gt; in your current directory. The &lt;code&gt;ubuntu/jammy64&lt;/code&gt; box is Ubuntu 22.04 LTS — a solid, well-maintained base image pulled from the &lt;a href="https://app.vagrantup.com/boxes/search" rel="noopener noreferrer"&gt;Vagrant Cloud&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Configure the Vagrantfile
&lt;/h2&gt;

&lt;p&gt;Open the generated &lt;code&gt;Vagrantfile&lt;/code&gt; and replace its contents with this well-commented configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Vagrant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

  &lt;span class="c1"&gt;# ── Base Box ────────────────────────────────────────────────────&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu/jammy64"&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;box_check_update&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;

  &lt;span class="c1"&gt;# ── Networking ──────────────────────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# Access your VM's web server at http://localhost:8080 on the host&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;network&lt;/span&gt; &lt;span class="s2"&gt;"forwarded_port"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;guest: &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;network&lt;/span&gt; &lt;span class="s2"&gt;"forwarded_port"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;guest: &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;  &lt;span class="c1"&gt;# Node/React&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;network&lt;/span&gt; &lt;span class="s2"&gt;"forwarded_port"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;guest: &lt;/span&gt;&lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="mi"&gt;5432&lt;/span&gt;  &lt;span class="c1"&gt;# PostgreSQL&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;network&lt;/span&gt; &lt;span class="s2"&gt;"forwarded_port"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;guest: &lt;/span&gt;&lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="mi"&gt;6379&lt;/span&gt;  &lt;span class="c1"&gt;# Redis&lt;/span&gt;

  &lt;span class="c1"&gt;# Private network — access VM directly at this IP from the host&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;network&lt;/span&gt; &lt;span class="s2"&gt;"private_network"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ip: &lt;/span&gt;&lt;span class="s2"&gt;"192.168.56.10"&lt;/span&gt;

  &lt;span class="c1"&gt;# ── Shared Folders ──────────────────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# Your project folder syncs automatically into the VM&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synced_folder&lt;/span&gt; &lt;span class="s2"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/vagrant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s2"&gt;"virtualbox"&lt;/span&gt;

  &lt;span class="c1"&gt;# ── Provider Settings (VirtualBox) ──────────────────────────────&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"virtualbox"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev-playground"&lt;/span&gt;
    &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2048"&lt;/span&gt;   &lt;span class="c1"&gt;# 2 GB RAM — adjust as needed&lt;/span&gt;
    &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cpus&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="c1"&gt;# Faster DNS resolution inside the VM&lt;/span&gt;
    &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;customize&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"modifyvm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--natdnshostresolver1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"on"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ── Provisioning ────────────────────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# This shell script runs ONCE when you first `vagrant up`&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provision&lt;/span&gt; &lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;inline: &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;SHELL&lt;/span&gt;&lt;span class="sh"&gt;
    echo "==&amp;gt; Updating package lists..."
    apt-get update -qq

    echo "==&amp;gt; Installing core tools..."
    apt-get install -y -qq \
      git curl wget unzip build-essential \
      software-properties-common apt-transport-https

    echo "==&amp;gt; Installing Node.js 20.x..."
    curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
    apt-get install -y nodejs

    echo "==&amp;gt; Installing Python 3 + pip..."
    apt-get install -y python3 python3-pip python3-venv

    echo "==&amp;gt; Installing Docker..."
    curl -fsSL https://get.docker.com | sh
    usermod -aG docker vagrant

    echo "==&amp;gt; Installing PostgreSQL..."
    apt-get install -y postgresql postgresql-contrib
    sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';"
    sudo -u postgres psql -c "CREATE DATABASE devdb;" 2&amp;gt;/dev/null || true

    echo "==&amp;gt; Installing Redis..."
    apt-get install -y redis-server
    sed -i 's/^bind 127.0.0.1/bind 0.0.0.0/' /etc/redis/redis.conf
    systemctl restart redis-server

    echo "==&amp;gt; All done! Your playground is ready."
&lt;/span&gt;&lt;span class="no"&gt;  SHELL&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; The &lt;code&gt;&amp;lt;&amp;lt;-SHELL&lt;/code&gt; block is a Bash heredoc — any valid shell script goes here. You can also point to an external script file with &lt;code&gt;config.vm.provision "shell", path: "provision.sh"&lt;/code&gt; to keep things tidy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 3 — Boot the VM
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vagrant up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first run will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download the &lt;code&gt;ubuntu/jammy64&lt;/code&gt; base box (~500 MB, cached locally for future use)&lt;/li&gt;
&lt;li&gt;Create and configure a VirtualBox VM&lt;/li&gt;
&lt;li&gt;Run the provisioning script&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Subsequent &lt;code&gt;vagrant up&lt;/code&gt; calls take only &lt;strong&gt;5–10 seconds&lt;/strong&gt; since the box is already downloaded and provisioned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Connect to Your VM
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vagrant ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're now inside the VM. Your project folder is mounted at &lt;code&gt;/vagrant&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /vagrant
&lt;span class="nb"&gt;ls&lt;/span&gt;        &lt;span class="c"&gt;# same files as your host machine!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything you install globally inside the VM is &lt;strong&gt;isolated&lt;/strong&gt; from your host OS. Clean and reproducible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 — Verify Your Stack
&lt;/h2&gt;

&lt;p&gt;Once SSH'd in, quickly verify the tools are running:&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;# Node.js&lt;/span&gt;
node &lt;span class="nt"&gt;--version&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# Python&lt;/span&gt;
python3 &lt;span class="nt"&gt;--version&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip3 &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# Docker&lt;/span&gt;
docker &lt;span class="nt"&gt;--version&lt;/span&gt;
docker run hello-world

&lt;span class="c"&gt;# PostgreSQL&lt;/span&gt;
psql &lt;span class="nt"&gt;-U&lt;/span&gt; postgres &lt;span class="nt"&gt;-h&lt;/span&gt; localhost &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\l&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Redis&lt;/span&gt;
redis-cli ping    &lt;span class="c"&gt;# should return PONG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From your &lt;strong&gt;host machine&lt;/strong&gt;, you can connect to services using the forwarded ports:&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;# PostgreSQL from host (use any GUI like TablePlus, DBeaver)&lt;/span&gt;
psql &lt;span class="nt"&gt;-h&lt;/span&gt; localhost &lt;span class="nt"&gt;-p&lt;/span&gt; 5432 &lt;span class="nt"&gt;-U&lt;/span&gt; postgres &lt;span class="nt"&gt;-d&lt;/span&gt; devdb

&lt;span class="c"&gt;# Redis from host&lt;/span&gt;
redis-cli &lt;span class="nt"&gt;-h&lt;/span&gt; localhost &lt;span class="nt"&gt;-p&lt;/span&gt; 6379 ping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6 — Everyday Workflow
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vagrant up&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start the VM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vagrant ssh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSH into the VM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vagrant halt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gracefully shut down the VM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vagrant reload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart the VM (picks up Vagrantfile changes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vagrant provision&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-run the provisioning script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vagrant destroy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Delete&lt;/strong&gt; the VM entirely (Vagrantfile stays)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vagrant snapshot save &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save a snapshot of the current VM state&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Reprovisioning:&lt;/strong&gt; If you update the &lt;code&gt;Vagrantfile&lt;/code&gt; provisioning script, run &lt;code&gt;vagrant reload --provision&lt;/code&gt; to apply changes without destroying the VM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 7 — Share Your Environment with the Team
&lt;/h2&gt;

&lt;p&gt;The beauty of this setup: commit your &lt;code&gt;Vagrantfile&lt;/code&gt; (and any &lt;code&gt;provision.sh&lt;/code&gt; scripts) to your repository. Anyone with Vagrant + VirtualBox installed runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/yourorg/your-repo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;your-repo
vagrant up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and they have an &lt;strong&gt;identical&lt;/strong&gt; environment to yours. No more "works on my machine."&lt;/p&gt;

&lt;p&gt;A minimal &lt;code&gt;.gitignore&lt;/code&gt; for Vagrant projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;.&lt;span class="n"&gt;vagrant&lt;/span&gt;/
*.&lt;span class="n"&gt;log&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: Multi-Machine Setup
&lt;/h2&gt;

&lt;p&gt;Vagrant can spin up multiple VMs in a single &lt;code&gt;Vagrantfile&lt;/code&gt; — perfect for simulating a microservices or client/server architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Vagrant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu/jammy64"&lt;/span&gt;
    &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;network&lt;/span&gt; &lt;span class="s2"&gt;"private_network"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ip: &lt;/span&gt;&lt;span class="s2"&gt;"192.168.56.11"&lt;/span&gt;
    &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provision&lt;/span&gt; &lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;inline: &lt;/span&gt;&lt;span class="s2"&gt;"apt-get update &amp;amp;&amp;amp; apt-get install -y nginx"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt; &lt;span class="s2"&gt;"db"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;|&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;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu/jammy64"&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;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;network&lt;/span&gt; &lt;span class="s2"&gt;"private_network"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ip: &lt;/span&gt;&lt;span class="s2"&gt;"192.168.56.12"&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;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provision&lt;/span&gt; &lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;inline: &lt;/span&gt;&lt;span class="s2"&gt;"apt-get update &amp;amp;&amp;amp; apt-get install -y postgresql"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start all machines with &lt;code&gt;vagrant up&lt;/code&gt;, or target one with &lt;code&gt;vagrant up web&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;VirtualBox kernel module error on Linux:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; /sbin/vboxconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Slow synced folder performance on macOS:&lt;/strong&gt;&lt;br&gt;
Consider switching to NFS: &lt;code&gt;config.vm.synced_folder ".", "/vagrant", type: "nfs"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Port already in use:&lt;/strong&gt;&lt;br&gt;
Change the &lt;code&gt;host:&lt;/code&gt; port number in the &lt;code&gt;forwarded_port&lt;/code&gt; config, or stop the conflicting service on your host.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Box not found" error:&lt;/strong&gt;&lt;br&gt;
Search available boxes at &lt;a href="https://app.vagrantup.com/boxes/search" rel="noopener noreferrer"&gt;app.vagrantup.com/boxes/search&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;You now have a fully portable, version-controlled development playground that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs identically on any OS&lt;/li&gt;
&lt;li&gt;Installs your entire stack automatically on first boot&lt;/li&gt;
&lt;li&gt;Forwards ports so host tools work seamlessly&lt;/li&gt;
&lt;li&gt;Syncs your code files in real time&lt;/li&gt;
&lt;li&gt;Can be destroyed and recreated in minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you're comfortable with this setup, consider exploring &lt;strong&gt;Ansible provisioning&lt;/strong&gt; for more complex stacks, or &lt;strong&gt;Docker-in-Vagrant&lt;/strong&gt; for a hybrid container + VM workflow.&lt;/p&gt;

&lt;p&gt;Happy hacking! 🚀&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Found this useful? Drop a ❤️ and share it with a teammate who's still fighting environment issues. Questions or improvements? Leave a comment below!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>vagrant</category>
      <category>devops</category>
      <category>linux</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Deploy Your Portfolio Website: GitHub Pages + Cloudflare Custom Domain</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Mon, 11 May 2026 23:46:58 +0000</pubDate>
      <link>https://dev.to/durrello/deploy-your-devops-portfolio-website-github-pages-cloudflare-custom-domain-1gnj</link>
      <guid>https://dev.to/durrello/deploy-your-devops-portfolio-website-github-pages-cloudflare-custom-domain-1gnj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Talent Forge Program · Project 1&lt;/strong&gt; | Beginner → Intermediate&lt;br&gt;
&lt;em&gt;The exact project that proves you can ship. Tie it to you, your resume, and your brand, all in one afternoon.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why This Project Matters
&lt;/h2&gt;

&lt;p&gt;Before you can list "built production infrastructure" on your CV, you need somewhere to list it &lt;em&gt;from&lt;/em&gt;. Your portfolio website is not a vanity project, it is proof of work. It demonstrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can version-control a codebase on GitHub&lt;/li&gt;
&lt;li&gt;You understand Cloudflare Pages, a real edge deployment platform used at scale in production&lt;/li&gt;
&lt;li&gt;You can configure DNS, custom domains, and SSL, skills that appear in every infrastructure job posting&lt;/li&gt;
&lt;li&gt;You have a working CI/CD pipeline: every &lt;code&gt;git push&lt;/code&gt; to &lt;code&gt;main&lt;/code&gt; triggers an automatic global deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For my talk at &lt;strong&gt;AWS Student Community Day&lt;/strong&gt; on &lt;em&gt;Learning DevOps the Right Way: Building in Public and Gaining Real Experience&lt;/em&gt;, this is what I pointed students to. Not because it is the flashiest, but because it is the one you can finish in a weekend, deploy publicly, and put at the top of your CV immediately.&lt;/p&gt;

&lt;p&gt;This is the complete implementation guide for &lt;strong&gt;Talent Forge Program 1&lt;/strong&gt;, the portfolio website project. I am walking you through exactly how I built and deployed &lt;a href="https://durrellgemuh.com" rel="noopener noreferrer"&gt;durrellgemuh.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Will Build
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your HTML/CSS/JS Site
        │
        ▼
   GitHub Repository
        │
        │  git push triggers
        ▼
   Cloudflare Pages (build + deploy)
        ├── Global CDN (300+ edge locations)
        ├── Automatic HTTPS (free SSL)
        ├── DDoS protection
        └── Custom domain
        │
        ▼
   yourdomain.com — live on the internet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;HTML, CSS, JavaScript — static site, no backend, no server&lt;/li&gt;
&lt;li&gt;GitHub — source control and the CI/CD trigger&lt;/li&gt;
&lt;li&gt;Cloudflare Pages — hosting, CDN, SSL, and deployment pipeline (all free)&lt;/li&gt;
&lt;li&gt;A custom domain from any registrar (Namecheap, Porkbun, Google Domains, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Cloudflare Pages?&lt;/strong&gt;&lt;br&gt;
Cloudflare Pages gives you a globally distributed CDN out of the box, automatic HTTPS with zero configuration, instant cache invalidation on every deploy, DDoS protection at the edge, and detailed analytics — all on the free tier. It is what production-minded engineers actually reach for when they need fast, reliable, zero-maintenance static hosting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; ~$10–15/year for the domain. Everything else is completely free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to complete:&lt;/strong&gt; 2–4 hours for the first deployment. Under 30 minutes for every update after that.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] A GitHub account — &lt;a href="https://github.com" rel="noopener noreferrer"&gt;github.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[ ] A Cloudflare account — &lt;a href="https://cloudflare.com" rel="noopener noreferrer"&gt;cloudflare.com&lt;/a&gt; (free plan)&lt;/li&gt;
&lt;li&gt;[ ] A registered custom domain — around $10–12/year from any registrar&lt;/li&gt;
&lt;li&gt;[ ] Git installed locally — &lt;a href="https://git-scm.com" rel="noopener noreferrer"&gt;git-scm.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[ ] VS Code or any code editor&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Part 1: Build Your Static Portfolio Site
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1.1 — Create Your Project Structure
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-portfolio
&lt;span class="nb"&gt;cd &lt;/span&gt;my-portfolio

&lt;span class="c"&gt;# Create the folder structure&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;css js img fonts

&lt;span class="c"&gt;# Create your main files&lt;/span&gt;
&lt;span class="nb"&gt;touch &lt;/span&gt;index.html css/style.css js/main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Your directory tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-portfolio/
├── index.html          ← entry point (Cloudflare Pages serves this)
├── css/
│   └── style.css
├── js/
│   └── main.js
├── img/
│   └── (your photos and project screenshots)
└── fonts/
    └── (any custom font files)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;This is exactly the structure used at &lt;a href="https://github.com/durrello/personal-website" rel="noopener noreferrer"&gt;github.com/durrello/personal-website&lt;/a&gt;&lt;/strong&gt; css, js, img, fonts, and multiple HTML pages. Fork it if you want a head start on the design.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 1.2 — Write Your index.html
&lt;/h3&gt;

&lt;p&gt;A minimal but complete portfolio template you can build on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Your Name — DevOps &amp;amp; Cloud Engineer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Your Name | DevOps Engineer&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"css/style.css"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- NAVIGATION --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nav-brand"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Your Name&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nav-links"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#about"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;About&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#projects"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Projects&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#blog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Blog&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#contact"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Contact&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- HERO SECTION --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"hero"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Your Name&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;DevOps &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Cloud Engineer&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
      Building scalable infrastructure on AWS and GCP.
      Kubernetes · Terraform · CI/CD · GitOps
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hero-cta"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#projects"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn-primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;View Projects&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"resume.pdf"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn-secondary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Download Resume&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- ABOUT SECTION --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"about"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;About Me&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
      Write 3–5 sentences. Who you are, what you work on, what you care about.
      Mention your current learning focus or the Talent Forge program.
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- PROJECTS SECTION --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"projects"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Projects&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"project-grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"project-card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Project Title&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Short description of what it does and the technologies used.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"project-tags"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;AWS&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Terraform&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Cloudflare Pages&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"project-links"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://github.com/yourrepo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;GitHub →&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://dev.to/yourarticle"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Blog Post →&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- BLOG SECTION --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"blog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Writing&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
      Links to your dev.to posts, LinkedIn articles, or any public writing.
      This section is proof that you build in public.
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- CONTACT SECTION --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"contact"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Get in Touch&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Email: you@email.com&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;LinkedIn: linkedin.com/in/yourhandle&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;GitHub: github.com/yourhandle&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"js/main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1.3 — Add Basic Styling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* css/style.css */&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'Segoe UI'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Tahoma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Geneva&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Verdana&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0b0b0f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e8e8f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt; &lt;span class="m"&gt;5%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sticky&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#1e1e2e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.nav-brand&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f6821f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Cloudflare orange */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.nav-links&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;list-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.nav-links&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e8e8f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.9rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.nav-links&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f6821f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#hero&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8rem&lt;/span&gt; &lt;span class="m"&gt;5%&lt;/span&gt; &lt;span class="m"&gt;6rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;800px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#hero&lt;/span&gt; &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;800&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#hero&lt;/span&gt; &lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#00d4ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#hero&lt;/span&gt; &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#9090a8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.hero-cta&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.btn-primary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f6821f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0b0b0f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.btn-primary&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.btn-secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#f6821f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f6821f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5rem&lt;/span&gt; &lt;span class="m"&gt;5%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;800&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#f6821f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.project-grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto-fill&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;320px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.project-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1a1a28&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#1e1e2e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-color&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.project-card&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f6821f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.project-card&lt;/span&gt; &lt;span class="nt"&gt;h3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.project-tags&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.project-tags&lt;/span&gt; &lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;246&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;130&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;246&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;130&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f6821f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2rem&lt;/span&gt; &lt;span class="m"&gt;0.6rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.8rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.project-links&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.project-links&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f6821f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.9rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1.4 — Test Locally
&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;# Python local server (usually pre-installed)&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server 8000

&lt;span class="c"&gt;# Visit: http://localhost:8000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure everything renders correctly before pushing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: Push to GitHub
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 2.1 — Initialise Git
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-portfolio
git init
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: initial portfolio site"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2.2 — Create the GitHub Repository
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://github.com/new" rel="noopener noreferrer"&gt;github.com/new&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repository name: &lt;code&gt;my-portfolio&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Visibility: &lt;strong&gt;Public&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Do NOT initialise with a README — you already have files&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create repository&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2.3 — Push
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add origin https://github.com/YOUR_USERNAME/my-portfolio.git
git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt;
&lt;span class="c"&gt;# Should show: feat: initial portfolio site&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Part 3: Connect GitHub to Cloudflare Pages
&lt;/h2&gt;

&lt;p&gt;This is where the CI/CD magic happens. Cloudflare Pages connects directly to your GitHub repository and automatically deploys on every push to &lt;code&gt;main&lt;/code&gt;. No pipeline YAML to write — Cloudflare handles the deployment infrastructure for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3.1 — Create a New Cloudflare Pages Project
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log in to &lt;a href="https://cloudflare.com" rel="noopener noreferrer"&gt;cloudflare.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;In the left sidebar, go to &lt;strong&gt;Workers &amp;amp; Pages&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt; → &lt;strong&gt;Pages&lt;/strong&gt; tab → &lt;strong&gt;Connect to Git&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3.2 — Authorise GitHub
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Connect GitHub&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Cloudflare opens a GitHub OAuth window&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Only select repositories&lt;/strong&gt; and select your &lt;code&gt;my-portfolio&lt;/code&gt; repo&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Install &amp;amp; Authorize&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 You are granting Cloudflare read access to your repository so it can pull your code on each push. This is the same pattern used in every modern CI/CD integration — Cloudflare, Vercel, and Netlify all work this way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 3.3 — Configure the Build Settings
&lt;/h3&gt;

&lt;p&gt;After authorising, you will see the build configuration screen. For a pure static HTML/CSS/JS site:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Production branch&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Framework preset&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build command&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(leave empty)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build output directory&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(leave empty)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root directory&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(leave empty)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why no build command?&lt;/strong&gt; Your site is plain HTML/CSS/JS — there is nothing to compile. Cloudflare Pages deploys the files exactly as they are in your repo. If you later add a framework (React, Next.js, Hugo), you would set the build command and output directory at that point.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Click &lt;strong&gt;Save and Deploy&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3.4 — Watch the First Deployment
&lt;/h3&gt;

&lt;p&gt;Cloudflare Pages will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pull your code from GitHub&lt;/li&gt;
&lt;li&gt;Run the build (instant for static files)&lt;/li&gt;
&lt;li&gt;Deploy to the global edge network&lt;/li&gt;
&lt;li&gt;Give you a URL like: &lt;code&gt;https://my-portfolio-abc.pages.dev&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first deployment takes 30–60 seconds. Visit the &lt;code&gt;.pages.dev&lt;/code&gt; URL to confirm your site is live.&lt;/p&gt;

&lt;p&gt;From this point on, every &lt;code&gt;git push&lt;/code&gt; to &lt;code&gt;main&lt;/code&gt; automatically triggers a new deployment. You now have a fully managed CI/CD pipeline — no servers, no agents, no maintenance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 4: Add Your Domain to Cloudflare
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 4.1 — Add Your Domain to Cloudflare DNS
&lt;/h3&gt;

&lt;p&gt;If your domain is not already managed by Cloudflare:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Cloudflare dashboard, click &lt;strong&gt;Add a Site&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter your domain: &lt;code&gt;yourdomain.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Free&lt;/strong&gt; plan → Continue&lt;/li&gt;
&lt;li&gt;Cloudflare scans existing DNS records — review and keep any you need&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Continue to activation&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cloudflare will give you two nameservers, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aria.ns.cloudflare.com
jay.ns.cloudflare.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4.2 — Update Nameservers at Your Registrar
&lt;/h3&gt;

&lt;p&gt;Go to wherever you bought the domain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find &lt;strong&gt;Nameservers&lt;/strong&gt; or &lt;strong&gt;DNS Settings&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Replace the existing nameservers with Cloudflare's two nameservers&lt;/li&gt;
&lt;li&gt;Save&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Nameserver propagation takes 15 minutes to 48 hours — usually under an hour. Cloudflare sends an email when your domain is activated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 4.3 — Verify Propagation
&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;# Check your domain's nameservers&lt;/span&gt;
dig NS yourdomain.com +short

&lt;span class="c"&gt;# Expected output (Cloudflare nameservers):&lt;/span&gt;
&lt;span class="c"&gt;# aria.ns.cloudflare.com.&lt;/span&gt;
&lt;span class="c"&gt;# jay.ns.cloudflare.com.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use &lt;a href="https://www.whatsmydns.net" rel="noopener noreferrer"&gt;whatsmydns.net&lt;/a&gt; — search for your domain with NS record type.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 5: Connect Your Custom Domain to Cloudflare Pages
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 5.1 — Add the Custom Domain
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Workers &amp;amp; Pages&lt;/strong&gt; → your Pages project&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Custom domains&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Set up a custom domain&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter: &lt;code&gt;yourdomain.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Continue&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cloudflare will check if your domain is managed in your account. Since you added it in Part 4, it will be found automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5.2 — Cloudflare Creates the DNS Record Automatically
&lt;/h3&gt;

&lt;p&gt;This is one of the best parts of using Cloudflare Pages with a Cloudflare-managed domain: &lt;strong&gt;Cloudflare automatically creates the CNAME record&lt;/strong&gt; pointing your domain to your Pages project. You do not touch DNS manually.&lt;/p&gt;

&lt;p&gt;Cloudflare creates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CNAME  yourdomain.com  →  my-portfolio-abc.pages.dev  (Proxied ✅)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click &lt;strong&gt;Activate domain&lt;/strong&gt; to confirm.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5.3 — Add www Redirect (Recommended)
&lt;/h3&gt;

&lt;p&gt;To make &lt;code&gt;www.yourdomain.com&lt;/code&gt; also work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Custom domains&lt;/strong&gt; → &lt;strong&gt;Set up a custom domain&lt;/strong&gt; again&lt;/li&gt;
&lt;li&gt;Enter: &lt;code&gt;www.yourdomain.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Cloudflare adds a second CNAME automatically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To redirect www to apex permanently:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Rules&lt;/strong&gt; → &lt;strong&gt;Redirect Rules&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Create a rule: if &lt;code&gt;Hostname equals www.yourdomain.com&lt;/code&gt; → redirect to &lt;code&gt;https://yourdomain.com&lt;/code&gt; (301 permanent)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Part 6: Configure SSL and Security
&lt;/h2&gt;

&lt;p&gt;Cloudflare Pages handles SSL automatically — your site gets HTTPS the moment the custom domain is activated. No certificate purchasing, no Let's Encrypt commands, no renewal reminders. Cloudflare renews it automatically, forever.&lt;/p&gt;

&lt;p&gt;Tighten these settings to production standard:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6.1 — SSL/TLS Mode
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to your domain in Cloudflare → &lt;strong&gt;SSL/TLS&lt;/strong&gt; → &lt;strong&gt;Overview&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set encryption mode to &lt;strong&gt;Full (strict)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cloudflare Pages serves over HTTPS natively, so Full (strict) works without any issue and is the most secure mode available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6.2 — Force HTTPS
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SSL/TLS&lt;/strong&gt; → &lt;strong&gt;Edge Certificates&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always Use HTTPS&lt;/strong&gt; → ON&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic HTTPS Rewrites&lt;/strong&gt; → ON&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Any visitor hitting &lt;code&gt;http://yourdomain.com&lt;/code&gt; is now redirected to &lt;code&gt;https://&lt;/code&gt; at Cloudflare's edge — before a request even touches your site.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6.3 — HSTS (Advanced — Optional)
&lt;/h3&gt;

&lt;p&gt;For maximum security, enable HTTP Strict Transport Security:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SSL/TLS&lt;/strong&gt; → &lt;strong&gt;Edge Certificates&lt;/strong&gt; → &lt;strong&gt;HTTP Strict Transport Security (HSTS)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enable HSTS → set &lt;strong&gt;Max Age&lt;/strong&gt; to 6 months&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;Include subdomains&lt;/strong&gt; if you have subdomains&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ HSTS tells browsers to never connect over HTTP for the specified period. Only enable this when HTTPS is fully confirmed and working.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part 7: Performance Settings
&lt;/h2&gt;

&lt;p&gt;These take under five minutes and have real-world impact:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speed → Optimization:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Auto Minify — HTML&lt;/td&gt;
&lt;td&gt;ON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto Minify — CSS&lt;/td&gt;
&lt;td&gt;ON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto Minify — JavaScript&lt;/td&gt;
&lt;td&gt;ON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brotli compression&lt;/td&gt;
&lt;td&gt;ON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Early Hints&lt;/td&gt;
&lt;td&gt;ON&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Caching → Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Caching Level&lt;/td&gt;
&lt;td&gt;Standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser Cache TTL&lt;/td&gt;
&lt;td&gt;4 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Security → Settings:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Security Level&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bot Fight Mode&lt;/td&gt;
&lt;td&gt;ON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email Address Obfuscation&lt;/td&gt;
&lt;td&gt;ON&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Bot Fight Mode silently challenges known bad bots at the edge. It is free, requires zero maintenance, and you should enable it on every site you run.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part 8: Verify the Full Setup
&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;# 1. Site loads over HTTPS with Cloudflare headers&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://yourdomain.com

&lt;span class="c"&gt;# Expected:&lt;/span&gt;
&lt;span class="c"&gt;# HTTP/2 200&lt;/span&gt;
&lt;span class="c"&gt;# server: cloudflare&lt;/span&gt;
&lt;span class="c"&gt;# content-type: text/html; charset=utf-8&lt;/span&gt;
&lt;span class="c"&gt;# cf-ray: &amp;lt;id&amp;gt;-&amp;lt;datacenter&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;# 2. HTTP redirects to HTTPS&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://yourdomain.com

&lt;span class="c"&gt;# Expected:&lt;/span&gt;
&lt;span class="c"&gt;# HTTP/1.1 301 Moved Permanently&lt;/span&gt;
&lt;span class="c"&gt;# location: https://yourdomain.com/&lt;/span&gt;

&lt;span class="c"&gt;# 3. www redirects to apex&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://www.yourdomain.com

&lt;span class="c"&gt;# Expected: 301 to https://yourdomain.com&lt;/span&gt;

&lt;span class="c"&gt;# 4. SSL certificate is valid&lt;/span&gt;
openssl s_client &lt;span class="nt"&gt;-connect&lt;/span&gt; yourdomain.com:443 &lt;span class="nt"&gt;-brief&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"Verification|subject"&lt;/span&gt;

&lt;span class="c"&gt;# 5. Check which Cloudflare PoP is serving you&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"Status: %{http_code} — CF-Ray: %header{cf-ray}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the Cloudflare dashboard you now have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Analytics&lt;/strong&gt; → real-time requests, bandwidth, unique visitors, cache hit ratio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; → threats blocked, bot traffic, firewall events&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pages → Deployments&lt;/strong&gt; → full deployment history, build logs, preview URLs for every branch&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 9: Your Ongoing Deployment Workflow
&lt;/h2&gt;

&lt;p&gt;Every future update follows this pattern:&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;# 1. Make your change locally&lt;/span&gt;
vim index.html        &lt;span class="c"&gt;# add a new project, update bio, etc.&lt;/span&gt;

&lt;span class="c"&gt;# 2. Commit with a meaningful message&lt;/span&gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: add HashiCorp Vault HA project card"&lt;/span&gt;

&lt;span class="c"&gt;# 3. Push to GitHub&lt;/span&gt;
git push

&lt;span class="c"&gt;# 4. Cloudflare Pages detects the push and deploys automatically&lt;/span&gt;
&lt;span class="c"&gt;# Monitor at: dash.cloudflare.com → Workers &amp;amp; Pages → your project → Deployments&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deployment completes in under 30 seconds. The new version is live globally across 300+ Cloudflare edge locations simultaneously.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is exactly how production static sites are deployed at scale.&lt;/strong&gt; You just built the same pipeline engineering teams use at startups and enterprises. Write it on your CV.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 10: What Content to Put On It
&lt;/h2&gt;

&lt;p&gt;Now that infrastructure is running, populate it with real content in this priority order:&lt;/p&gt;

&lt;h3&gt;
  
  
  Projects Section
&lt;/h3&gt;

&lt;p&gt;For each project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One-sentence description of the problem it solves&lt;/li&gt;
&lt;li&gt;Technologies used — be specific: "Cloudflare Pages" not just "CDN"&lt;/li&gt;
&lt;li&gt;Link to the GitHub repository&lt;/li&gt;
&lt;li&gt;Link to your dev.to write-up if you published one&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Site shows a Cloudflare error instead of your portfolio
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Confirm the custom domain is activated: Workers &amp;amp; Pages → Custom domains → green checkmark&lt;/li&gt;
&lt;li&gt;Check DNS has propagated: &lt;code&gt;dig yourdomain.com CNAME&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Wait 5–10 minutes and hard refresh&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Build fails in Cloudflare Pages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Deployments&lt;/strong&gt; tab → click the failed deployment → &lt;strong&gt;View build log&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;For a static site with no build command, the most common cause is a misconfigured output directory&lt;/li&gt;
&lt;li&gt;Ensure Build output directory is empty (not &lt;code&gt;dist&lt;/code&gt; or &lt;code&gt;build&lt;/code&gt; — those are for frameworks)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Custom domain not activating
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Confirm nameservers are on Cloudflare: &lt;code&gt;dig NS yourdomain.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check your domain shows &lt;strong&gt;Active&lt;/strong&gt; status under &lt;strong&gt;Websites&lt;/strong&gt; in Cloudflare&lt;/li&gt;
&lt;li&gt;The domain must be active in Cloudflare before Pages custom domain recognition works&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mixed content warnings
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;All &lt;code&gt;src=&lt;/code&gt; and &lt;code&gt;href=&lt;/code&gt; attributes must use &lt;code&gt;https://&lt;/code&gt; or protocol-relative &lt;code&gt;//&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Cloudflare's Automatic HTTPS Rewrites handles most of these automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Changes not appearing after push
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Check the Deployments tab — did the build succeed?&lt;/li&gt;
&lt;li&gt;Hard refresh: &lt;code&gt;Ctrl + Shift + R&lt;/code&gt; (Windows/Linux) / &lt;code&gt;Cmd + Shift + R&lt;/code&gt; (Mac)&lt;/li&gt;
&lt;li&gt;Go to Caching → &lt;strong&gt;Purge Everything&lt;/strong&gt; in Cloudflare if the old version persists&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Architecture Summary
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You (developer)
      │
      │  git push origin main
      ▼
GitHub Repository (source of truth)
      │
      │  Cloudflare Pages webhook detects push
      ▼
Cloudflare Pages Build Pipeline
  ├── Pulls latest code from GitHub
  ├── Runs build (instant for static HTML/CSS/JS)
  └── Deploys to Cloudflare edge network
      │
      ▼
Cloudflare Edge (300+ PoPs globally)
  ├── Serves yourdomain.com
  ├── SSL/TLS — Full (strict)
  ├── CDN caching
  ├── Always-HTTPS redirect
  ├── DDoS protection (always-on)
  ├── Bot Fight Mode
  └── Brotli compression
      │
      ▼
End User — fast, secure, globally distributed ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What This Proves on Your CV
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Personal Portfolio Website&lt;/strong&gt; — Deployed static site via Cloudflare Pages with GitHub CI/CD integration. Configured custom domain, Full (strict) SSL, HSTS, DDoS protection, bot filtering, Brotli compression, and edge caching. Site globally distributed across 300+ Cloudflare PoPs with automated deployments triggered on every commit to &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is seven production skills in a project that costs $10/year to run.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add Cloudflare Web Analytics&lt;/strong&gt; — Analytics → Web Analytics → add your site. Free, privacy-respecting, no cookie banner needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add a contact form&lt;/strong&gt; — &lt;a href="https://formspree.io" rel="noopener noreferrer"&gt;Formspree&lt;/a&gt; or &lt;a href="https://web3forms.com" rel="noopener noreferrer"&gt;Web3Forms&lt;/a&gt; work without any backend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write your first blog post&lt;/strong&gt; and link it from the writing section — closes the loop on building in public&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add a Projects page&lt;/strong&gt; — every Talent Forge project you complete gets a card here&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project 2:&lt;/strong&gt; Containerise a real application with Docker and deploy via a full CI/CD pipeline&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/pages/" rel="noopener noreferrer"&gt;Cloudflare Pages Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/pages/get-started/git-integration/" rel="noopener noreferrer"&gt;Cloudflare Pages — Git Integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/pages/configuration/custom-domains/" rel="noopener noreferrer"&gt;Cloudflare Pages — Custom Domains&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/ssl/" rel="noopener noreferrer"&gt;Cloudflare SSL/TLS Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Reference Implementation: &lt;a href="https://github.com/durrello/personal-website" rel="noopener noreferrer"&gt;github.com/durrello/personal-website&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live Example: &lt;a href="https://durrellgemuh.com" rel="noopener noreferrer"&gt;durrellgemuh.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  About the Talent Forge Program
&lt;/h2&gt;

&lt;p&gt;This article is the official solution guide for &lt;strong&gt;Project 1&lt;/strong&gt; of the &lt;a href="https://durrellgemuh.com/page-talent-forge-admin" rel="noopener noreferrer"&gt;Talent Forge mentorship program&lt;/a&gt; — a free, structured DevOps curriculum run through &lt;a href="https://nextgenplayground.org" rel="noopener noreferrer"&gt;NextGen Playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The program takes you from zero to a portfolio that gets you hired — through real projects, real infrastructure, and real documentation you can point employers at.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Join the program:&lt;/strong&gt; &lt;a href="https://discord.com/invite/fWUEh5x9PD" rel="noopener noreferrer"&gt;discord.com/invite/fWUEh5x9PD&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Enrol:&lt;/strong&gt; &lt;a href="https://forms.gle/XuD5a6nxi67xf7fY7" rel="noopener noreferrer"&gt;forms.gle/XuD5a6nxi67xf7fY7&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The GitLab variant (GitLab Pages + custom domain) is the companion article.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloudflare</category>
      <category>github</category>
      <category>cicd</category>
    </item>
    <item>
      <title>GKE Gateway API: Full Setup &amp; Troubleshooting Runbook</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Mon, 27 Apr 2026 11:04:59 +0000</pubDate>
      <link>https://dev.to/durrello/gke-gateway-api-full-setup-troubleshooting-runbook-145j</link>
      <guid>https://dev.to/durrello/gke-gateway-api-full-setup-troubleshooting-runbook-145j</guid>
      <description>&lt;p&gt;I recently went through the full process of setting up GKE Gateway API on a production-grade cluster — Fleet registration, controller enablement, Helm deployment, and a handful of painful debugging sessions. This is the cleaned-up runbook so you don't have to learn these lessons the hard way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cluster Context
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Project&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;my-gcp-project&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cluster&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;my-gke-cluster&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Region&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;us-east1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Namespace&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;my-namespace&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Traffic flow:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Client → Cloud Load Balancer → GKE Gateway (L7) → HTTPRoute → Kubernetes Service → Pod&lt;/code&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Prerequisites &amp;amp; Initial Setup
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Set project &amp;amp; cluster context
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project my-gcp-project

gcloud container clusters get-credentials my-gke-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Enable required GCP APIs
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud services &lt;span class="nb"&gt;enable &lt;/span&gt;container.googleapis.com
gcloud services &lt;span class="nb"&gt;enable &lt;/span&gt;gkehub.googleapis.com
gcloud services &lt;span class="nb"&gt;enable &lt;/span&gt;serviceusage.googleapis.com
gcloud services &lt;span class="nb"&gt;enable &lt;/span&gt;multiclusteringress.googleapis.com
gcloud services &lt;span class="nb"&gt;enable &lt;/span&gt;multiclusterservicediscovery.googleapis.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  IAM permissions
&lt;/h3&gt;

&lt;p&gt;Grant your service account the roles needed for Fleet and Ingress management:&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="nv"&gt;SA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;serviceAccount:my-service-account@my-gcp-project.iam.gserviceaccount.com
&lt;span class="nv"&gt;PROJECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-gcp-project

gcloud projects add-iam-policy-binding &lt;span class="nv"&gt;$PROJECT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"roles/container.admin"&lt;/span&gt;

gcloud projects add-iam-policy-binding &lt;span class="nv"&gt;$PROJECT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"roles/gkehub.admin"&lt;/span&gt;

gcloud projects add-iam-policy-binding &lt;span class="nv"&gt;$PROJECT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"roles/serviceusage.serviceUsageAdmin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Fleet Registration &amp;amp; Gateway Enablement
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Enable Workload Identity
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;The Workload Identity pool must match your &lt;strong&gt;service project&lt;/strong&gt;, not the host project.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters update my-gke-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--workload-pool&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-service-project.svc.id.goog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Register the cluster to Fleet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container fleet memberships register my-gke-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--gke-cluster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east1/my-gke-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-workload-identity&lt;/span&gt;

&lt;span class="c"&gt;# Verify&lt;/span&gt;
gcloud container fleet memberships list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable Fleet Ingress (Gateway controller)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container fleet ingress &lt;span class="nb"&gt;enable&lt;/span&gt;

&lt;span class="c"&gt;# Verify — expected: state: ACTIVE, membershipStates: OK&lt;/span&gt;
gcloud container fleet ingress describe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable Gateway API at cluster level
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a separate step from Fleet Ingress. Without it, &lt;code&gt;kubectl get gatewayclass&lt;/code&gt;&lt;br&gt;
returns nothing and the controller will never attach.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters update my-gke-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-gateway-api&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable a release channel
&lt;/h3&gt;

&lt;p&gt;The GKE-managed Gateway controller &lt;strong&gt;requires&lt;/strong&gt; a release channel to attach to the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters update my-gke-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--release-channel&lt;/span&gt; regular
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install Gateway API CRDs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/kubernetes-sigs/gateway-api/releases/latest/download/standard-install.yaml

&lt;span class="c"&gt;# Verify&lt;/span&gt;
kubectl get crds | &lt;span class="nb"&gt;grep &lt;/span&gt;gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify GatewayClasses
&lt;/h3&gt;

&lt;p&gt;After the controller attaches, these should appear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get gatewayclass

&lt;span class="c"&gt;# NAME                                  CONTROLLER&lt;/span&gt;
&lt;span class="c"&gt;# gke-l7-rilb                           networking.gke.io/gateway&lt;/span&gt;
&lt;span class="c"&gt;# gke-l7-global-external-managed        networking.gke.io/gateway&lt;/span&gt;
&lt;span class="c"&gt;# gke-l7-gxlb                           networking.gke.io/gateway&lt;/span&gt;
&lt;span class="c"&gt;# gke-l7-regional-external-managed      networking.gke.io/gateway&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Helm Chart Deployment
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm upgrade &lt;span class="nt"&gt;--install&lt;/span&gt; my-gateway-chart ./gateway &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace &lt;span class="nt"&gt;--create-namespace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Validate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get gateway    &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace
kubectl get httproute  &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace
kubectl get svc        &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace
kubectl describe gateway &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Issues &amp;amp; Fixes
&lt;/h2&gt;

&lt;p&gt;This is where things got interesting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 1 — GatewayClass missing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; &lt;code&gt;kubectl get gatewayclass&lt;/code&gt; returns no resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Fleet Ingress and Gateway API at cluster level are two separate&lt;br&gt;
enablement steps. You need both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Run &lt;code&gt;--enable-gateway-api&lt;/code&gt; on the cluster (step 2 above).&lt;/p&gt;


&lt;h3&gt;
  
  
  Issue 2 — Gateway stuck "Waiting for controller"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; ArgoCD shows Gateway as &lt;code&gt;Progressing&lt;/code&gt; indefinitely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; No release channel was set. The managed controller won't attach&lt;br&gt;
without one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Set the release channel, then delete the Gateway resource and let ArgoCD&lt;br&gt;
re-sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete gateway my-https-gateway &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace
&lt;span class="c"&gt;# ArgoCD will recreate it against the now-attached controller&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Issue 3 — CertificateMap region mismatch
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CertificateMap "my-cert-map-cert-map" must not be configured in a region other than global
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things went wrong here simultaneously.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem A — Helm double-suffix bug&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Helm template was concatenating a suffix onto a value that already had it:&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;# values.yaml&lt;/span&gt;
&lt;span class="na"&gt;certMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-cert-map&lt;/span&gt;

&lt;span class="c1"&gt;# template (broken)&lt;/span&gt;
&lt;span class="na"&gt;networking.gke.io/certmap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.gateway.certMap&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;-cert-map&lt;/span&gt;
&lt;span class="c1"&gt;# renders as: my-cert-map-cert-map ❌&lt;/span&gt;

&lt;span class="c1"&gt;# template (correct)&lt;/span&gt;
&lt;span class="na"&gt;networking.gke.io/certmap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.gateway.certMap&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="c1"&gt;# renders as: my-cert-map ✔&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem B — Wrong GatewayClass&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gke-l7-rilb&lt;/code&gt; is regional internal. CertificateMaps are global-only — they are&lt;br&gt;
incompatible:&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;# WRONG&lt;/span&gt;
&lt;span class="na"&gt;gatewayClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gke-l7-rilb&lt;/span&gt;            &lt;span class="c1"&gt;# regional internal, no certMap&lt;/span&gt;

&lt;span class="c1"&gt;# CORRECT&lt;/span&gt;
&lt;span class="na"&gt;gatewayClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gke-l7-global-external-managed&lt;/span&gt;  &lt;span class="c1"&gt;# global, certMap supported&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Rule to remember:&lt;/strong&gt; CertificateMaps are &lt;strong&gt;GLOBAL ONLY&lt;/strong&gt;. Never pair them&lt;br&gt;
with a regional GatewayClass.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Issue 4 — HTTPRoute BackendNotFound
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BackendNotFound: services my-namespace/&amp;lt;name&amp;gt; not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; The &lt;code&gt;backendRefs&lt;/code&gt; in the HTTPRoute used Helm value names, not the&lt;br&gt;
actual Kubernetes Service names.&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;# WRONG — these were Helm value names, not Service names&lt;/span&gt;
&lt;span class="na"&gt;backendRefs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend-service&lt;/span&gt;   &lt;span class="c1"&gt;# ❌&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;search-service&lt;/span&gt;     &lt;span class="c1"&gt;# ❌&lt;/span&gt;

&lt;span class="c1"&gt;# CORRECT — exact Kubernetes Service names&lt;/span&gt;
&lt;span class="na"&gt;backendRefs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;           &lt;span class="c1"&gt;# ✔&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;search&lt;/span&gt;             &lt;span class="c1"&gt;# ✔&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dashboard&lt;/span&gt;          &lt;span class="c1"&gt;# ✔&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;analytics-web&lt;/span&gt;      &lt;span class="c1"&gt;# ✔&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Validation &amp;amp; Testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Get the Gateway IP
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get gateway &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace
&lt;span class="c"&gt;# Look at the ADDRESS column&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why a bare curl returns 404
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://&amp;lt;GATEWAY-IP&amp;gt;
&lt;span class="c"&gt;# 404 fault filter abort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is &lt;strong&gt;expected&lt;/strong&gt;. The Gateway matched the request but no HTTPRoute matched&lt;br&gt;
the Host header. It's not an error — it confirms the Gateway is working.&lt;/p&gt;
&lt;h3&gt;
  
  
  Test with the correct Host header
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Host: myapp.dev.example.io"&lt;/span&gt; http://&amp;lt;GATEWAY-IP&amp;gt;
&lt;span class="c"&gt;# Should return your app's HTML&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  DNS
&lt;/h3&gt;

&lt;p&gt;Create an A record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myapp.dev.example.io  →  &amp;lt;GATEWAY-IP&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  HTTPS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-vk&lt;/span&gt; https://myapp.dev.example.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Local pod test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/analytics-web 8080:3000 &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace
curl http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. ArgoCD Cleanup
&lt;/h2&gt;

&lt;p&gt;Orphaned HTTPRoutes from previous deploys will cause ArgoCD to show &lt;code&gt;Degraded&lt;/code&gt;&lt;br&gt;
even when the app is healthy. Clean them up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete httproute old-frontend-route  &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace
kubectl delete httproute old-search-route    &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or enable pruning in your ArgoCD Application:&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;syncPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;syncOptions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PruneLast=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Quick Debug Reference
&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;# Gateway status&lt;/span&gt;
kubectl describe gateway   &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace
kubectl get gateway &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace &lt;span class="nt"&gt;-w&lt;/span&gt;

&lt;span class="c"&gt;# HTTPRoutes&lt;/span&gt;
kubectl describe httproute &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace

&lt;span class="c"&gt;# Controller (no pods = managed mode, that's normal)&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-A&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; gateway
kubectl get gatewayclass
kubectl get crd | &lt;span class="nb"&gt;grep &lt;/span&gt;gateway

&lt;span class="c"&gt;# Cluster config&lt;/span&gt;
gcloud container clusters describe my-gke-cluster &lt;span class="nt"&gt;--region&lt;/span&gt; us-east1

&lt;span class="c"&gt;# Events&lt;/span&gt;
kubectl get events &lt;span class="nt"&gt;-n&lt;/span&gt; my-namespace &lt;span class="nt"&gt;--sort-by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.lastTimestamp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. Final State
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GatewayClass&lt;/td&gt;
&lt;td&gt;✅ Accepted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gateway&lt;/td&gt;
&lt;td&gt;✅ Programmed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTPRoute&lt;/td&gt;
&lt;td&gt;✅ Healthy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Services&lt;/td&gt;
&lt;td&gt;✅ Resolved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load Balancer&lt;/td&gt;
&lt;td&gt;✅ Active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pods&lt;/td&gt;
&lt;td&gt;✅ Running&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArgoCD&lt;/td&gt;
&lt;td&gt;✅ Synced&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  9. Key Lessons
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fleet Ingress ≠ Gateway controller ready.&lt;/strong&gt; You need &lt;code&gt;--enable-gateway-api&lt;/code&gt; separately on the cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No release channel = no controller attachment.&lt;/strong&gt; Add the cluster to &lt;code&gt;regular&lt;/code&gt; or &lt;code&gt;stable&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CertificateMaps are global only.&lt;/strong&gt; Never use them with &lt;code&gt;gke-l7-rilb&lt;/code&gt; or any regional GatewayClass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helm string concatenation silently breaks GCP resource names.&lt;/strong&gt; Always &lt;code&gt;helm template&lt;/code&gt; and inspect the rendered output before applying.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTPRoute &lt;code&gt;backendRefs&lt;/code&gt; need exact Service names.&lt;/strong&gt; Your Helm value names and your Kubernetes Service names are not the same thing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;404 fault filter abort = hostname mismatch&lt;/strong&gt;, not a broken Gateway. Check your Host header.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No gateway pods in GKE managed mode is normal.&lt;/strong&gt; The controller is
cloud-managed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataplane V2 can't be enabled on an existing cluster.&lt;/strong&gt; You'd need to
recreate it — and it's not required for Gateway API anyway.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  10. Improvements Worth Making
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Wildcard hostname&lt;/strong&gt; — Reduces per-service HTTPRoute config significantly:&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;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hostnames&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.dev.example.io"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CI validation&lt;/strong&gt; — Add a pre-deploy check that verifies every &lt;code&gt;backendRef&lt;/code&gt; name exists as a live Service in the target namespace. This eliminates the BackendNotFound class of errors before they hit the cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Helm schema guards&lt;/strong&gt; — Use &lt;code&gt;values.schema.json&lt;/code&gt; to validate that your &lt;code&gt;certMap&lt;/code&gt; value doesn't already end with the suffix your template appends. Catches double-suffix bugs at &lt;code&gt;helm lint&lt;/code&gt; time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unified naming&lt;/strong&gt; — Make your Helm release name, Kubernetes Service name, and HTTPRoute &lt;code&gt;backendRef&lt;/code&gt; all derive from the same value. One source of truth, zero drift.&lt;/p&gt;




&lt;p&gt;Hope this saves someone a few hours. The GKE docs cover each of these pieces individually but the interactions between them — especially Fleet Ingress vs cluster-level Gateway API enablement, and the CertificateMap GatewayClass constraint — aren't obvious until you hit them.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>gke</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Getting Started with Amazon CloudWatch</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Sun, 12 Apr 2026 01:56:26 +0000</pubDate>
      <link>https://dev.to/durrello/getting-started-with-amazon-cloudwatch-5cca</link>
      <guid>https://dev.to/durrello/getting-started-with-amazon-cloudwatch-5cca</guid>
      <description>&lt;p&gt;Monitoring is one of the most overlooked parts of building systems.&lt;/p&gt;

&lt;p&gt;Until something breaks.&lt;/p&gt;

&lt;p&gt;Then suddenly, it becomes the most important thing in your entire stack.&lt;/p&gt;

&lt;p&gt;If you're working in AWS, one tool sits at the center of this:&lt;/p&gt;

&lt;p&gt;Amazon CloudWatch&lt;/p&gt;

&lt;p&gt;This post breaks down what CloudWatch actually does, how teams use it in real environments, and how you can get started without overcomplicating things.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why CloudWatch Matters
&lt;/h2&gt;

&lt;p&gt;In real-world systems, you need to answer three critical questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is happening right now?&lt;/li&gt;
&lt;li&gt;What happened earlier?&lt;/li&gt;
&lt;li&gt;When should I react?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CloudWatch helps you answer all three.&lt;/p&gt;

&lt;p&gt;Without monitoring, you're operating blind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Components of CloudWatch
&lt;/h2&gt;

&lt;p&gt;You don’t need to learn everything at once. Focus on these four:&lt;/p&gt;

&lt;h3&gt;
  
  
  Metrics: What’s Happening?
&lt;/h3&gt;

&lt;p&gt;Metrics are numerical data points collected over time.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU utilization (EC2)&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;Request count&lt;/li&gt;
&lt;li&gt;Error rates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You use metrics to understand system behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logs: What Happened?
&lt;/h3&gt;

&lt;p&gt;Logs give you detailed insight into events inside your system.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application logs&lt;/li&gt;
&lt;li&gt;System logs&lt;/li&gt;
&lt;li&gt;Container logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Logs are what you check when something goes wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alarms: When to React
&lt;/h3&gt;

&lt;p&gt;Alarms trigger actions based on metrics.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU &amp;gt; 80%&lt;/li&gt;
&lt;li&gt;Error rate spike&lt;/li&gt;
&lt;li&gt;Instance down&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alarms help you move from reactive → proactive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dashboards: One View
&lt;/h3&gt;

&lt;p&gt;Dashboards combine metrics and visualizations into one place.&lt;/p&gt;

&lt;p&gt;They help teams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor systems in real-time&lt;/li&gt;
&lt;li&gt;Share visibility across teams&lt;/li&gt;
&lt;li&gt;Track key performance indicators&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Use Case
&lt;/h2&gt;

&lt;p&gt;Let’s say you deploy an application on AWS.&lt;/p&gt;

&lt;p&gt;Here’s how CloudWatch fits in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 sends CPU and network metrics&lt;/li&gt;
&lt;li&gt;Your app sends logs to CloudWatch Logs&lt;/li&gt;
&lt;li&gt;You create alarms for:

&lt;ul&gt;
&lt;li&gt;high CPU&lt;/li&gt;
&lt;li&gt;failed requests&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;You build a dashboard to visualize everything&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Now you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;visibility&lt;/li&gt;
&lt;li&gt;alerts&lt;/li&gt;
&lt;li&gt;debugging capability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s production-ready thinking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;p&gt;Most beginners:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try to configure everything at once&lt;/li&gt;
&lt;li&gt;Ignore logs&lt;/li&gt;
&lt;li&gt;Create too many alarms&lt;/li&gt;
&lt;li&gt;Don’t test alerting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep it simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Get Started (Practical)
&lt;/h2&gt;

&lt;p&gt;Start with this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enable default metrics for EC2 or your service&lt;/li&gt;
&lt;li&gt;Send application logs to CloudWatch Logs&lt;/li&gt;
&lt;li&gt;Create 1–2 alarms (CPU, errors)&lt;/li&gt;
&lt;li&gt;Build a simple dashboard&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s enough to begin.&lt;/p&gt;

&lt;p&gt;You can scale later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pro Tip
&lt;/h2&gt;

&lt;p&gt;Monitoring is not something you “add later.”&lt;/p&gt;

&lt;p&gt;It’s part of the system design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;CloudWatch is not just a tool.&lt;/p&gt;

&lt;p&gt;It’s how you understand your system.&lt;/p&gt;

&lt;p&gt;And if you don’t understand your system — you can’t operate it.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>cloudwatch</category>
      <category>cloud</category>
    </item>
    <item>
      <title>What does it really cost to become a DevOps engineer? Getting started in DevOps without spending a dime</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Sun, 05 Apr 2026 09:25:51 +0000</pubDate>
      <link>https://dev.to/durrello/what-does-it-really-cost-to-become-a-devops-engineer-getting-started-in-devops-without-spending-a-1p0a</link>
      <guid>https://dev.to/durrello/what-does-it-really-cost-to-become-a-devops-engineer-getting-started-in-devops-without-spending-a-1p0a</guid>
      <description>&lt;p&gt;Let's be real, when most people Google "how to become a DevOps engineer," they land on listicles selling $500 Udemy courses, $300/year certification prep, and cloud subscriptions that bill you before you've written your first pipeline.&lt;/p&gt;

&lt;p&gt;Here's the truth: you don't need to spend anything to get started. Zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is DevOps, really?
&lt;/h2&gt;

&lt;p&gt;DevOps is a culture, a set of practices, and a toolchain that bridges software development and IT operations. It's about shipping faster, breaking less, and recovering quickly when things go wrong. Learnable for free.&lt;/p&gt;

&lt;h2&gt;
  
  
  OS and virtualization
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ubuntu / Debian&lt;/strong&gt;  The go-to Linux distro for learning. Free, well-documented, massive community.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VirtualBox&lt;/strong&gt;  Run Linux VMs on any machine. Free and cross-platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WSL2&lt;/strong&gt;  Run a full Linux environment inside Windows. No VM overhead needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multipass&lt;/strong&gt;  Instantly spin up Ubuntu VMs on Mac, Windows, or Linux. Free CLI tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Version control and collaboration
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Git&lt;/strong&gt;  The foundation. Learn it before anything else. Free and open source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub / GitLab (free tier)&lt;/strong&gt; Host repos, run CI/CD pipelines, and build your public portfolio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gitea&lt;/strong&gt;  Self-hosted GitHub alternative. Great for practicing on-premise Git workflows.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Containers and orchestration
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker (community edition)&lt;/strong&gt;  Build, ship, and run containers. Free for personal use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Podman&lt;/strong&gt; — A daemonless Docker alternative. Free and increasingly enterprise-standard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minikube / kind / k3s&lt;/strong&gt; — Run Kubernetes locally. No cloud bill needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Play with Docker / Play with Kubernetes&lt;/strong&gt; — Browser-based sandboxes. Zero install, zero cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CI/CD pipelines
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Actions&lt;/strong&gt; Free for public repos. The best starting point for CI/CD.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitLab CI/CD&lt;/strong&gt; 400 free CI minutes per month. Built-in, no plugin setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins&lt;/strong&gt; Open source, self-hosted. Still widely used in enterprise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tekton&lt;/strong&gt; Kubernetes-native CI/CD. Open source and cloud-agnostic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Infrastructure as code
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terraform / OpenTofu&lt;/strong&gt; — Define cloud infra as code. OpenTofu is the open source fork — free forever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ansible&lt;/strong&gt; — Agentless automation for config management and deployments. Open source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pulumi (free tier)&lt;/strong&gt; — IaC using real programming languages (Python, TypeScript, Go).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Monitoring and observability
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus&lt;/strong&gt; Open source metrics collection and alerting. The industry standard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grafana (open source)&lt;/strong&gt; Build dashboards. Completely free self-hosted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loki&lt;/strong&gt; Like Prometheus, but for logs. Free and open source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Netdata&lt;/strong&gt; Real-time system monitoring with zero config.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenTelemetry&lt;/strong&gt; Open standard for traces, metrics, and logs. Vendor-neutral and free.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cloud free tiers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Free Tier&lt;/strong&gt; 12 months of EC2, S3, Lambda, and more at no cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Cloud Free Tier&lt;/strong&gt; $300 credit for 90 days plus an always-free e2-micro VM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure for Students&lt;/strong&gt; $100 credit, no credit card required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Oracle Cloud Free Tier&lt;/strong&gt; Two free AMD VMs forever. The most generous always-free compute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fly.io / Render / Railway&lt;/strong&gt; Deploy real apps for free. Great for portfolio projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scripting and languages
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bash&lt;/strong&gt; Ships with every Linux system. Learn this first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python&lt;/strong&gt; Used everywhere in DevOps for automation and tooling. Free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go (Golang)&lt;/strong&gt; Docker, Kubernetes, and Terraform are written in Go. Free to learn and use.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Free learning platforms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;roadmap.sh/devops&lt;/strong&gt; The community-maintained DevOps roadmap. Start here for direction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KodeKloud (free tier)&lt;/strong&gt; Hands-on browser labs for Docker, Kubernetes, and Ansible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux Foundation / edX&lt;/strong&gt; LFS101 and several Kubernetes intro courses are free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FreeCodeCamp&lt;/strong&gt; Free courses and YouTube content on Linux and cloud fundamentals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;90DaysOfDevOps (GitHub)&lt;/strong&gt; A 90-day structured learning journey. Completely free on GitHub.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Skill Builder / Google Cloud Skills Boost&lt;/strong&gt; Official free learning paths from AWS and Google.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Build something. Anything.
&lt;/h2&gt;

&lt;p&gt;Deploy a personal blog with Docker and GitHub Actions. Set up a Prometheus and Grafana monitoring stack for a side project. Automate something annoying. Put it on GitHub. That portfolio gets you hired — not the course completion certificate.&lt;/p&gt;

&lt;p&gt;The barrier to DevOps is time and consistency, not money.&lt;/p&gt;

&lt;p&gt;What questions do you have about getting started? Drop them in the comments, happy to help.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>beginners</category>
      <category>career</category>
      <category>linux</category>
    </item>
    <item>
      <title>AWS Service Spotlight: AWS Systems Manager (SSM)</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Thu, 26 Mar 2026 13:32:40 +0000</pubDate>
      <link>https://dev.to/durrello/aws-service-spotlight-aws-systems-manager-ssm-3850</link>
      <guid>https://dev.to/durrello/aws-service-spotlight-aws-systems-manager-ssm-3850</guid>
      <description>&lt;p&gt;Welcome to my &lt;strong&gt;AWS Service Spotlight&lt;/strong&gt; series, where I break down AWS services, how they work, when to use them, and how they fit into real-world DevOps systems.&lt;/p&gt;

&lt;p&gt;This week we're talking about &lt;strong&gt;AWS Systems Manager (SSM)&lt;/strong&gt; one of those services that quietly does a ton of heavy lifting in production environments, yet doesn't always get the spotlight it deserves.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is AWS Systems Manager?
&lt;/h2&gt;

&lt;p&gt;Simply put, SSM is AWS's &lt;strong&gt;operations hub for managing your infrastructure at scale&lt;/strong&gt;. Think of it as a remote control for your EC2 instances — and a whole lot more.&lt;/p&gt;

&lt;p&gt;More technically: SSM is a collection of tools that lets you automate operational tasks, run commands across fleets of instances, manage configuration, patch systems, and access instances securely — all without needing a bastion host or open SSH/RDP ports.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use It?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem It Solves
&lt;/h3&gt;

&lt;p&gt;Managing dozens or hundreds of servers manually is a nightmare. You'd need to SSH into each one, run scripts, hope nothing breaks, and repeat. SSM eliminates that entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use it when you need to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run commands across many instances simultaneously&lt;/li&gt;
&lt;li&gt;Install software or agents on a fleet without manual access&lt;/li&gt;
&lt;li&gt;Access instances that have no public IP or open ports&lt;/li&gt;
&lt;li&gt;Automate patching and compliance checks&lt;/li&gt;
&lt;li&gt;Store and retrieve secrets and config values securely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Who should care:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DevOps and platform engineers managing cloud infrastructure&lt;/li&gt;
&lt;li&gt;Security teams who want auditability and zero open ports&lt;/li&gt;
&lt;li&gt;Anyone deploying software to EC2 at scale&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How I Used It This Week
&lt;/h2&gt;

&lt;p&gt;This week I had a real, practical challenge: &lt;strong&gt;deploy the Datadog monitoring agent across a mixed fleet of Linux and Windows EC2 instances&lt;/strong&gt; — in a way that any AWS account could run, without hardcoding credentials or writing instance-specific scripts.&lt;/p&gt;

&lt;p&gt;Here's what I did:&lt;/p&gt;

&lt;p&gt;I created &lt;strong&gt;two public SSM Command documents&lt;/strong&gt; — one for Linux, one for Windows — and published them from a central AWS account with public permissions, so they're callable by ARN from literally any AWS account in the world.&lt;/p&gt;

&lt;p&gt;Each document accepts just two parameters at runtime:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DDApiKey&lt;/code&gt; — the Datadog API key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DDSite&lt;/code&gt; — the Datadog intake region (defaults to &lt;code&gt;datadoghq.com&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Linux, the document runs the official Datadog shell installer via &lt;code&gt;curl&lt;/code&gt;. For Windows, it uses &lt;code&gt;msiexec&lt;/code&gt; with the Datadog MSI package — and getting the PowerShell quoting right (outer single quotes, inner escaped double quotes) was the key to making it work reliably through SSM.&lt;/p&gt;

&lt;p&gt;The result: a &lt;strong&gt;one-click, parameterized, cross-OS monitoring deployment&lt;/strong&gt; that any team can run against their fleet in minutes — no SSH, no RDP, no manual steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step: How I Built and Published the SSM Documents
&lt;/h2&gt;

&lt;p&gt;Here's exactly how I did it — entirely through the AWS Console, no CLI required.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create the Linux Document
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;AWS Console → Systems Manager → Documents&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Create document" → "Command or Session"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Fill in the details:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt;: &lt;code&gt;InstallDatadogAgent-Linux&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document type&lt;/strong&gt;: Command document&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content format&lt;/strong&gt;: JSON&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Paste the following content:
&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;"schemaVersion"&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.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Installs the Datadog Agent (v7) on Linux."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&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;"DDApiKey"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(Required) Your Datadog API Key"&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;"DDSite"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Datadog intake site."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"datadoghq.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allowedValues"&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;"datadoghq.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"datadoghq.eu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"us3.datadoghq.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"us5.datadoghq.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ap1.datadoghq.com"&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;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mainSteps"&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="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;"aws:runShellScript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"InstallDatadogLinux"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"inputs"&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;"runCommand"&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;"DD_API_KEY={{ DDApiKey }} DD_SITE={{ DDSite }} DD_AGENT_MAJOR_VERSION=7 bash -c &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$(curl -L https://install.datadoghq.com/scripts/install_script_agent7.sh)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&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="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="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;ol&gt;
&lt;li&gt;Click &lt;strong&gt;"Create document"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Create the Windows Document
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Back in &lt;strong&gt;Documents → Create document → Command or Session&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Fill in the details:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt;: &lt;code&gt;InstallDatadogAgent-Windows&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document type&lt;/strong&gt;: Command document&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content format&lt;/strong&gt;: JSON&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Paste the following content:
&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;"schemaVersion"&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.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Installs the Datadog Agent (v7) on Windows."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&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;"DDApiKey"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(Required) Your Datadog API Key"&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;"DDSite"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Datadog intake site."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"datadoghq.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allowedValues"&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;"datadoghq.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"datadoghq.eu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"us3.datadoghq.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"us5.datadoghq.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ap1.datadoghq.com"&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;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mainSteps"&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="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;"aws:runPowerShellScript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"InstallDatadogWindows"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"inputs"&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;"runCommand"&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;"$p = Start-Process -Wait -PassThru msiexec -ArgumentList '/qn /i &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://windows-agent.datadoghq.com/datadog-agent-7-latest.amd64.msi&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; /log C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Windows&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;SystemTemp&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;install-datadog.log APIKEY=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;{{ DDApiKey }}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; SITE=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;{{ DDSite }}&lt;/span&gt;&lt;span class="se"&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="s2"&gt;"if ($p.ExitCode -ne 0) {"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"  Write-Host &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;msiexec failed with exit code $($p.ExitCode). Check C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Windows&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;SystemTemp&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;install-datadog.log&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; -ForegroundColor Red"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"  exit $p.ExitCode"&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="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;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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;"Create document"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3: Make Both Documents Public
&lt;/h3&gt;

&lt;p&gt;This is the step that makes the documents usable from &lt;strong&gt;any AWS account in the world&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;each document&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Documents → Owned by me&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click the document name&lt;/li&gt;
&lt;li&gt;Go to the &lt;strong&gt;"Permissions"&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Public&lt;/strong&gt; acknowledge and save.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;That's it. The document is now publicly accessible via its ARN.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 4: Copy the Document ARN
&lt;/h3&gt;

&lt;p&gt;On each document's detail page, copy the ARN. It looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arn:aws:ssm:us-east-1:123456789012:document/InstallDatadogAgent-Linux
arn:aws:ssm:us-east-1:123456789012:document/InstallDatadogAgent-Windows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anyone in any AWS account can now reference these ARNs directly in Run Command — no need to copy or recreate the documents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Run the Documents
&lt;/h3&gt;

&lt;p&gt;From &lt;strong&gt;any AWS account&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Systems Manager → Run Command&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Run command"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;In the search box → select &lt;strong&gt;"Document name prefix"&lt;/strong&gt; → paste the full ARN&lt;/li&gt;
&lt;li&gt;Fill in parameters:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DDApiKey&lt;/code&gt; → your Datadog API key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DDSite&lt;/code&gt; → &lt;code&gt;datadoghq.com&lt;/code&gt; (or your region)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Targets&lt;/strong&gt; → choose instances by tag or select manually&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Run"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  A Note on Document Content
&lt;/h3&gt;

&lt;p&gt;The two documents are intentionally kept &lt;strong&gt;minimal and focused&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No hardcoded values&lt;/strong&gt; — API key and site are always passed at runtime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate documents per OS&lt;/strong&gt; — avoids the SSM &lt;code&gt;precondition&lt;/code&gt; quirk that causes false failures on mixed fleets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No environment or tag parameters&lt;/strong&gt; — kept lean so anyone can run it without knowing your internal tagging conventions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux&lt;/strong&gt; uses the official Datadog shell installer — the same script you'd run manually&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows&lt;/strong&gt; uses the official MSI installer via PowerShell's &lt;code&gt;Start-Process&lt;/code&gt; — the quoting pattern (&lt;code&gt;outer single quotes&lt;/code&gt;, &lt;code&gt;inner escaped double quotes&lt;/code&gt;) is critical for SSM to pass the arguments correctly to &lt;code&gt;msiexec&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  IAM Requirement
&lt;/h3&gt;

&lt;p&gt;Before SSM can communicate with an instance, the instance needs an IAM Role attached with this single policy:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;AmazonSSMManagedInstanceCore&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To set it up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;IAM → Roles → Create role → EC2&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Attach &lt;code&gt;AmazonSSMManagedInstanceCore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Name it &lt;code&gt;EC2-SSM-Role&lt;/code&gt; → Create&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EC2 → Select instance → Actions → Security → Modify IAM role&lt;/strong&gt; → attach the role&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No reboot needed. SSM will recognize the instance within about a minute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Real-World Use Cases
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DevOps Pipelines&lt;/strong&gt;&lt;br&gt;
Trigger SSM Run Command from a CI/CD pipeline to deploy application updates across an auto-scaling group after a build completes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kubernetes (EKS)&lt;/strong&gt;&lt;br&gt;
Use SSM Session Manager to access EKS worker nodes securely without exposing SSH. Great for debugging node-level issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security &amp;amp; Compliance&lt;/strong&gt;&lt;br&gt;
Use SSM Patch Manager to automatically patch OS vulnerabilities on a schedule and audit compliance across your fleet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secrets &amp;amp; Config Management&lt;/strong&gt;&lt;br&gt;
Store database passwords, API keys, and feature flags in SSM Parameter Store. Pull them securely at runtime in Lambda, ECS, or EC2 — no hardcoded secrets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incident Response&lt;/strong&gt;&lt;br&gt;
Use Run Command to instantly restart services, collect logs, or run diagnostics across an entire fleet during an incident — in seconds, not hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hybrid &amp;amp; On-Prem&lt;/strong&gt;&lt;br&gt;
SSM works with on-premises servers too via Hybrid Activations. Manage your data center the same way you manage your cloud.&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Run Command&lt;/strong&gt; — execute scripts across any number of instances simultaneously&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session Manager&lt;/strong&gt; — browser-based terminal, no SSH keys or open ports needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameter Store&lt;/strong&gt; — secure storage for config values and secrets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Patch Manager&lt;/strong&gt; — automated OS patching with compliance reporting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Manager&lt;/strong&gt; — enforce desired configuration state continuously&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributor&lt;/strong&gt; — package and deploy software agents at scale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documents&lt;/strong&gt; — reusable, versionable, shareable automation scripts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public Documents&lt;/strong&gt; — shareable across any AWS account via ARN&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  How It Works (High-Level)
&lt;/h2&gt;

&lt;p&gt;Every EC2 instance runs an &lt;strong&gt;SSM Agent&lt;/strong&gt; (pre-installed on most modern AMIs). This agent maintains a persistent, outbound-only connection to the SSM service endpoints over HTTPS.&lt;/p&gt;

&lt;p&gt;When you trigger a Run Command or Session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You (Console/API)
      ↓
AWS SSM Service
      ↓
SSM Agent on Instance (outbound HTTPS — no inbound ports needed)
      ↓
Executes command, streams output back
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The instance needs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SSM Agent&lt;/strong&gt; installed and running&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Role&lt;/strong&gt; with &lt;code&gt;AmazonSSMManagedInstanceCore&lt;/code&gt; policy attached&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outbound HTTPS&lt;/strong&gt; (port 443) to SSM endpoints&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No VPN, no bastion, no open security group rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration with Other AWS Services
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;How SSM Works With It&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EC2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Core target — manage instances directly via agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Role-based access controls who can run what documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stream command output to S3 for long-running jobs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudWatch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Send SSM logs and metrics to CloudWatch for alerting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EKS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Access worker nodes securely via Session Manager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pull config/secrets from Parameter Store at function runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EventBridge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Trigger SSM automations on schedule or in response to events&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AWS Alternatives:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS OpsWorks&lt;/strong&gt; — configuration management using Chef/Puppet, heavier setup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Config&lt;/strong&gt; — focused on compliance auditing, not execution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EC2 User Data&lt;/strong&gt; — runs scripts at launch only, not on-demand&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Non-AWS Alternatives:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ansible&lt;/strong&gt; — powerful but requires network access and more setup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chef / Puppet&lt;/strong&gt; — enterprise config management, complex overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform&lt;/strong&gt; — infrastructure provisioning, not runtime operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SSM wins when you're already in AWS and want zero additional infrastructure to manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  When NOT to Use It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You need &lt;strong&gt;complex configuration management&lt;/strong&gt; with dependency resolution — Ansible or Chef handles that better&lt;/li&gt;
&lt;li&gt;Your instances are &lt;strong&gt;not on AWS&lt;/strong&gt; and you don't want Hybrid Activations overhead&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;real-time streaming logs&lt;/strong&gt; — CloudWatch or a dedicated log agent is better suited&lt;/li&gt;
&lt;li&gt;You're managing &lt;strong&gt;containers directly&lt;/strong&gt; — native ECS/EKS tooling is more appropriate&lt;/li&gt;
&lt;li&gt;Your team is already deeply invested in Ansible — adding SSM creates duplication without enough gain&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;SSM is one of those services you don't fully appreciate until you've managed infrastructure without it. Once it clicks — the secure access, the fleet-wide automation, the public reusable documents — it becomes a default part of how you think about AWS operations.&lt;/p&gt;

&lt;p&gt;This week's use case was a great reminder that SSM isn't just for patching or basic scripts. With a little thought, you can build reusable, cross-account, cross-OS automation that scales to any team or environment.&lt;/p&gt;

&lt;p&gt;If your EC2 instances don't have SSM set up yet, that's the first thing I'd fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Connect
&lt;/h2&gt;

&lt;p&gt;If this helped you think differently about AWS operations, drop a comment or share it with someone managing EC2 fleets.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;strong&gt;NextGen Playground&lt;/strong&gt; — a platform helping engineers gain real-world DevOps experience through hands-on projects, mentorship, and practical learning.&lt;/p&gt;

&lt;p&gt;If you're trying to level up your cloud and DevOps skills with real projects, not just tutorials — check it out and let's build together. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>ssm</category>
      <category>devops</category>
      <category>datadog</category>
    </item>
  </channel>
</rss>
