<?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.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>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>
    <item>
      <title>Building a Complete Monitoring Stack with Prometheus and Grafana using Docker</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Wed, 18 Mar 2026 22:47:57 +0000</pubDate>
      <link>https://dev.to/durrello/building-a-complete-monitoring-stack-with-prometheus-and-grafana-using-docker-36h8</link>
      <guid>https://dev.to/durrello/building-a-complete-monitoring-stack-with-prometheus-and-grafana-using-docker-36h8</guid>
      <description>&lt;p&gt;Monitoring your infrastructure shouldn't be complicated. In this guide, I'll show you how to spin up a complete monitoring solution with &lt;strong&gt;Prometheus&lt;/strong&gt; and &lt;strong&gt;Grafana&lt;/strong&gt; in just a few commands using Docker Compose.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Get
&lt;/h2&gt;

&lt;p&gt;By the end of this tutorial, you'll have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus&lt;/strong&gt; for metrics collection and storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grafana&lt;/strong&gt; for beautiful dashboards and visualization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node Exporter&lt;/strong&gt; for system metrics (CPU, memory, disk)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx&lt;/strong&gt; with its own exporter for web server monitoring&lt;/li&gt;
&lt;li&gt;Everything containerized and easy to manage&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;You'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker and Docker Compose installed&lt;/li&gt;
&lt;li&gt;Basic understanding of containers&lt;/li&gt;
&lt;li&gt;5 minutes of your time &lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Our stack consists of 5 services working together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐
│   Grafana   │ (Port 3000)
│  Dashboard  │
└──────┬──────┘
       │ Queries
       ↓
┌─────────────┐      ┌──────────────┐
│ Prometheus  │◄─────┤ Node Exporter│ (System Metrics)
│   (Port     │      └──────────────┘
│    9090)    │
└──────┬──────┘      ┌──────────────┐
       └─────────────┤Nginx Exporter│ (Web Metrics)
                     └──────┬───────┘
                            ↓
                     ┌──────────────┐
                     │    Nginx     │ (Port 8080)
                     └──────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Clone the Repository
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://github.com/durrello/prometheus-grafana-docker.git
&lt;span class="nb"&gt;cd &lt;/span&gt;prometheus-grafana-docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Start Everything
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! Your monitoring stack is now running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing Your Services
&lt;/h2&gt;

&lt;p&gt;Once everything is up, you can access:&lt;/p&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;URL&lt;/th&gt;
&lt;th&gt;Credentials&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grafana&lt;/td&gt;
&lt;td&gt;&lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;admin / admin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prometheus&lt;/td&gt;
&lt;td&gt;&lt;a href="http://localhost:9090" rel="noopener noreferrer"&gt;http://localhost:9090&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node Exporter&lt;/td&gt;
&lt;td&gt;&lt;a href="http://localhost:9100/metrics" rel="noopener noreferrer"&gt;http://localhost:9100/metrics&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nginx&lt;/td&gt;
&lt;td&gt;&lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nginx Exporter&lt;/td&gt;
&lt;td&gt;&lt;a href="http://localhost:9113/metrics" rel="noopener noreferrer"&gt;http://localhost:9113/metrics&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Setting Up Your First Dashboard
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Add Prometheus as a Data Source
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log into Grafana (&lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Data Sources&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add data source&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Prometheus&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set URL to &lt;code&gt;http://prometheus:9090&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save &amp;amp; Test&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Import a Dashboard
&lt;/h3&gt;

&lt;p&gt;Grafana has thousands of pre-built dashboards. Here are some great ones to start with:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Node Exporter (System Metrics):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;+&lt;/strong&gt; → &lt;strong&gt;Import&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter dashboard ID: &lt;code&gt;1860&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select your Prometheus data source&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Import&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;For Nginx:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;+&lt;/strong&gt; → &lt;strong&gt;Import&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter dashboard ID: &lt;code&gt;12708&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select your Prometheus data source&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Import&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Understanding the Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Docker Compose Services
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prometheus:&lt;/strong&gt;&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;prometheus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prom/prometheus:latest&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml&lt;/span&gt;
  &lt;span class="na"&gt;ports&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;9090:9090"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Grafana:&lt;/strong&gt;&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;grafana&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana/grafana:latest&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GF_SECURITY_ADMIN_USER=admin&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GF_SECURITY_ADMIN_PASSWORD=admin&lt;/span&gt;
  &lt;span class="na"&gt;ports&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;3000:3000"&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;grafana-data:/var/lib/grafana&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prometheus Scrape Configuration
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;prometheus/prometheus.yml&lt;/code&gt; file defines what metrics to collect:&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;scrape_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prometheus'&lt;/span&gt;
    &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prometheus:9090'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;node_exporter'&lt;/span&gt;
    &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nodeexporter:9100'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nginx'&lt;/span&gt;
    &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nginxexporter:9113'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prometheus scrapes metrics every 15 seconds from these endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alerting (Bonus)
&lt;/h2&gt;

&lt;p&gt;The setup includes a sample alert configuration in &lt;code&gt;prometheus/alerts.yml&lt;/code&gt;:&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HighCPUUsage&lt;/span&gt;
  &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node_cpu_seconds_total{mode="idle"} &amp;lt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;
  &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1m&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;warning&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CPU&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;usage&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;high"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can extend this with more sophisticated alerts based on your needs!&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Commands
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;View logs:&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;docker-compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; prometheus
docker-compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; grafana
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Restart a service:&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;docker-compose restart prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Stop everything:&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;docker-compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Stop and remove volumes (clean slate):&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;docker-compose down &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Now that you have a working monitoring stack, you can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add more exporters&lt;/strong&gt; - MySQL, PostgreSQL, Redis, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create custom dashboards&lt;/strong&gt; - Visualize metrics specific to your apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up alerting&lt;/strong&gt; - Get notified via Slack, email, or PagerDuty&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor custom applications&lt;/strong&gt; - Instrument your code with Prometheus client libraries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale it up&lt;/strong&gt; - Deploy this to production with proper storage and HA setup&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;Docker Compose makes it trivial to run complex multi-service stacks&lt;/li&gt;
&lt;li&gt;Prometheus is pull-based - it scrapes metrics from exporters&lt;/li&gt;
&lt;li&gt;Grafana provides beautiful, flexible visualization&lt;/li&gt;
&lt;li&gt;Exporters bridge the gap between services and Prometheus&lt;/li&gt;
&lt;li&gt;This setup is production-ready with minimal tweaks (change default passwords!)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://prometheus.io/docs/" rel="noopener noreferrer"&gt;Prometheus Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://grafana.com/docs/" rel="noopener noreferrer"&gt;Grafana Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/prometheus/node_exporter" rel="noopener noreferrer"&gt;Node Exporter Metrics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://grafana.com/grafana/dashboards/" rel="noopener noreferrer"&gt;Grafana Dashboard Library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Have questions or suggestions? Drop them in the comments below! 👇&lt;/p&gt;

&lt;p&gt;If you found this helpful, give it a ❤️ and share it with your team!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>monitoring</category>
      <category>docker</category>
    </item>
    <item>
      <title>AWS Service Spotlight: Amazon S3</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Wed, 18 Mar 2026 13:22:19 +0000</pubDate>
      <link>https://dev.to/durrello/aws-service-spotlight-1-amazon-s3-the-glue-you-dont-think-about-until-you-need-it-1458</link>
      <guid>https://dev.to/durrello/aws-service-spotlight-1-amazon-s3-the-glue-you-dont-think-about-until-you-need-it-1458</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 starting with one of AWS's oldest and most underrated workhorses — &lt;strong&gt;Amazon S3&lt;/strong&gt;. Not because it's flashy, but because it saved me from a genuinely frustrating situation this week. More on that in a bit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Amazon S3?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Simple Storage Service&lt;/strong&gt; — that's what S3 stands for. And at its core, that's exactly what it is: a place to store files (called &lt;em&gt;objects&lt;/em&gt;) in the cloud, organized inside containers called &lt;em&gt;buckets&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Think of it like a USB drive that lives in the cloud, never loses data, scales infinitely, and can be accessed by anything with the right permissions.&lt;/p&gt;

&lt;p&gt;On a slightly more technical level: S3 is an &lt;strong&gt;object storage service&lt;/strong&gt;. Unlike block storage (like an EBS volume attached to your EC2) or file storage (like EFS), S3 stores data as discrete objects — each with its own key, metadata, and access rules. It's not mounted to a server. You interact with it via API, CLI, or SDK.&lt;/p&gt;

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

&lt;p&gt;S3 solves a deceptively simple problem: &lt;strong&gt;how do you store and share files reliably, at any scale, without managing infrastructure?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You'd reach for S3 when you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Share files between systems that can't talk to each other directly&lt;/li&gt;
&lt;li&gt;Store artifacts, logs, or backups that need to outlive the machine that created them&lt;/li&gt;
&lt;li&gt;Decouple storage from compute so your EC2 instances stay stateless&lt;/li&gt;
&lt;li&gt;Create a single source of truth for configuration files, scripts, or binaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Who should care?&lt;/strong&gt; DevOps engineers, backend developers, platform teams — anyone who touches infrastructure. S3 shows up everywhere once you start looking.&lt;/p&gt;

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

&lt;p&gt;This one came from a real headache.&lt;/p&gt;

&lt;p&gt;I was working with two EC2 instances — neither of them internet-facing. I needed to get a service running on a new instance that had been created from an AMI. The problem? The service binaries weren't baked into the AMI, and the original engineer who set this up had left without documenting where the installation files lived or sharing the EC2 key pair. No key pair access, no internet access, no obvious way to pull the files across.&lt;/p&gt;

&lt;p&gt;I could have gone down a rabbit hole — trying to recover access, hunting through old infrastructure, escalating to get the key pair — but S3 offered a much cleaner path.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;On the source EC2&lt;/strong&gt;, I used the AWS CLI to upload the installation files directly to an S3 bucket. Since the instance had an IAM role attached with S3 write permissions, no credentials were needed — it just worked.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; /path/to/installation/files s3://my-internal-bucket/service-installer/ &lt;span class="nt"&gt;--recursive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;On the target EC2&lt;/strong&gt;, I pulled the files down from the same bucket using the CLI, again via the attached IAM role.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://my-internal-bucket/service-installer/ /opt/service/ &lt;span class="nt"&gt;--recursive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Ran the installer, service came up, problem solved.
No SSH tunneling. No SCP across instances. No VPN gymnastics. S3 acted as a &lt;strong&gt;neutral transfer point&lt;/strong&gt; between two isolated machines — and because neither instance needed internet access to reach S3 (VPC endpoints handle that), the network boundaries were never a concern.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The extra step I took: I left the files in the bucket. If another engineer hits this same wall tomorrow, the files are there, documented, and accessible. That's the kind of thing that saves hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Real-World Use Cases
&lt;/h2&gt;

&lt;p&gt;S3 is one of those services that shows up in almost every architecture. Here's where you'll actually encounter it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD pipelines&lt;/strong&gt; — Storing build artifacts between pipeline stages (CodeBuild → S3 → CodeDeploy is a classic pattern)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes&lt;/strong&gt; — Storing Helm charts, kubeconfig backups, or persistent data exports from pods before teardown&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform / IaC&lt;/strong&gt; — Remote state backend for &lt;code&gt;terraform.tfstate&lt;/code&gt; files, shared across teams&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring &amp;amp; logs&lt;/strong&gt; — CloudWatch can ship logs to S3 for long-term retention and analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AMI bootstrapping&lt;/strong&gt; — Storing userdata scripts, config files, or binaries that EC2 instances pull on first boot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disaster recovery&lt;/strong&gt; — Backing up RDS snapshots, EBS snapshots, and configuration exports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static website hosting&lt;/strong&gt; — Frontend deployments without a server (paired with CloudFront)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data pipelines&lt;/strong&gt; — Landing zone for raw data before it hits Glue, Athena, or Redshift&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Virtually unlimited storage&lt;/strong&gt; — No capacity planning needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;11 nines of durability (99.999999999%)&lt;/strong&gt; — AWS redundantly stores your objects across multiple AZs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage classes&lt;/strong&gt; — S3 Standard, Infrequent Access, Glacier for cost optimization based on access frequency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioning&lt;/strong&gt; — Keep every version of every object; roll back anytime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle policies&lt;/strong&gt; — Automatically transition or delete objects based on age&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bucket policies &amp;amp; IAM&lt;/strong&gt; — Fine-grained access control at the bucket or object level&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event notifications&lt;/strong&gt; — Trigger Lambda, SQS, or SNS when objects are created or deleted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption&lt;/strong&gt; — At rest (SSE-S3, SSE-KMS) and in transit (HTTPS enforced)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC Endpoints&lt;/strong&gt; — Access S3 privately without internet traffic leaving your VPC&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How It Works (High-Level)
&lt;/h2&gt;

&lt;p&gt;When you upload a file to S3, you're putting an &lt;strong&gt;object&lt;/strong&gt; into a &lt;strong&gt;bucket&lt;/strong&gt;. A bucket is a globally unique namespace tied to a specific AWS region. The object gets a &lt;strong&gt;key&lt;/strong&gt; (essentially its path/filename) and is stored redundantly across multiple availability zones within that region.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your EC2 / App
     │
     ▼ (HTTPS via VPC Endpoint or Internet Gateway)
  S3 Bucket  ──────────────────────────────────────
  └── /service-installer/
       ├── service-v2.tar.gz
       └── install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access is controlled by a combination of &lt;strong&gt;IAM roles&lt;/strong&gt; (identity-based) and &lt;strong&gt;bucket policies&lt;/strong&gt; (resource-based). In most internal setups, you attach an IAM role to your EC2 and grant it &lt;code&gt;s3:GetObject&lt;/code&gt; or &lt;code&gt;s3:PutObject&lt;/code&gt; on specific buckets — no hard-coded credentials needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration with Other AWS Services
&lt;/h2&gt;

&lt;p&gt;S3 doesn't live in isolation. It's one of the most integrated services in the entire AWS ecosystem:&lt;/p&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 It Connects&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;Instances pull configs, scripts, and binaries via CLI or SDK using IAM roles&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;Roles and policies control who (and what) can read/write to buckets&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;Logs exported to S3 for archival; S3 request metrics visible in CloudWatch&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;S3 events trigger Lambda functions (e.g. process a file when it's uploaded)&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;Pods use S3 for artifact storage, backups, or as a data source via IRSA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CodePipeline / CodeBuild&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3 is the default artifact store between pipeline stages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudFront&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3 as an origin for CDN-cached static content delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Terraform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3 backend stores remote state; DynamoDB handles state locking&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;Within AWS:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EFS (Elastic File System)&lt;/strong&gt; — When you need a shared, mountable file system across multiple EC2s simultaneously&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EBS (Elastic Block Store)&lt;/strong&gt; — When you need block-level storage attached to a single instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FSx&lt;/strong&gt; — For Windows-native or high-performance file workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Outside AWS:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Cloud Storage&lt;/strong&gt; — GCP's equivalent, near-identical feature set&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure Blob Storage&lt;/strong&gt; — Microsoft's object storage offering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MinIO&lt;/strong&gt; — Open-source, S3-compatible object storage you can self-host on-prem or in Kubernetes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare R2&lt;/strong&gt; — S3-compatible with no egress fees; worth considering for high-download workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When NOT to Use It
&lt;/h2&gt;

&lt;p&gt;S3 is great, but it's not always the right tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You need a mounted file system&lt;/strong&gt; — S3 is not a file system. If your app expects to &lt;code&gt;open()&lt;/code&gt; files from a path like &lt;code&gt;/data/&lt;/code&gt;, use EFS or EBS instead. S3 FUSE mounts exist but add complexity and latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need low-latency, frequent random reads/writes&lt;/strong&gt; — S3 has per-request latency. For a database or high-throughput app, use EBS or a proper database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small files at very high frequency&lt;/strong&gt; — Thousands of tiny PUT/GET requests per second can get costly. Consider buffering or batching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need strict file locking&lt;/strong&gt; — S3 has no native file locking mechanism. Two processes writing the same key simultaneously will have one overwrite the other.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;S3 is one of those services that engineers often take for granted — until they're in a situation where nothing else will do. This week it got me out of a real bind: no key pair, no internet access, no documentation left behind by the previous engineer. S3 became the neutral ground that two isolated instances could both reach, and it took about ten minutes to sort out.&lt;/p&gt;

&lt;p&gt;The bigger lesson? Always leave things better than you found them. The files are in the bucket now. The next engineer won't have this problem.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>s3</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>How to Update an Elasticsearch/Kibana License Using Kibana Dev Tools</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Tue, 17 Mar 2026 08:49:14 +0000</pubDate>
      <link>https://dev.to/durrello/how-to-update-an-elasticsearchkibana-license-using-kibana-dev-tools-28bf</link>
      <guid>https://dev.to/durrello/how-to-update-an-elasticsearchkibana-license-using-kibana-dev-tools-28bf</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Licensing in the Elastic Stack (Elasticsearch + Kibana) controls access to advanced features such as security, alerting, machine learning, and enterprise capabilities. Licenses have expiration dates, and once expired, certain features may be limited or disabled.&lt;/p&gt;

&lt;p&gt;Updating your license ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Continued access to premium features&lt;/li&gt;
&lt;li&gt;Compliance with your subscription level&lt;/li&gt;
&lt;li&gt;Avoidance of service interruptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One important detail: &lt;strong&gt;licenses are applied at the cluster level, not per space or namespace.&lt;/strong&gt;&lt;br&gt;
This means &lt;strong&gt;updating the license once applies it globally across all spaces/namespaces in Kibana&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Log in to Kibana
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open your Kibana URL in a browser&lt;/li&gt;
&lt;li&gt;Log in with a user that has sufficient privileges.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Step 2: Select the Space (if applicable)
&lt;/h2&gt;

&lt;p&gt;If your Kibana instance uses multiple spaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the space selector (top-left corner)&lt;/li&gt;
&lt;li&gt;Choose any space you want&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important:&lt;br&gt;
Even though you select a specific space, the license update will apply to the entire cluster — &lt;strong&gt;all spaces will inherit the updated license&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3: Open Dev Tools
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In the left-hand menu, go to:
&lt;strong&gt;Management → Dev Tools&lt;/strong&gt; (or simply “Dev Tools” depending on your version)&lt;/li&gt;
&lt;li&gt;This opens the Console where you can run Elasticsearch API requests&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Step 4: Check Current License
&lt;/h2&gt;

&lt;p&gt;Run the following request to view the current license:&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="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;_license&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Important Note About License Format
&lt;/h2&gt;

&lt;p&gt;Sometimes the license you receive (for example from Elastic or a vendor) comes as a &lt;strong&gt;single-line JSON string&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example (hard to read / error-prone):&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="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"uid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"xxx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"enterprise"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"issue_date_in_millis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1700000000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"expiry_date_in_millis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1800000000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"signature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ABC..."&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;Before pasting it into Kibana Dev Tools, you should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Format (pretty-print) the JSON&lt;/li&gt;
&lt;li&gt;Ensure proper indentation&lt;/li&gt;
&lt;li&gt;Verify there are no missing brackets or quotes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Formatted example:&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;"license"&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;"uid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"&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;"enterprise"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"issue_date_in_millis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1700000000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expiry_date_in_millis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1800000000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"max_nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"max_resource_units"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"issued_to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your Company"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"issuer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"API"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"signature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REPLACE_WITH_SIGNATURE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start_date_in_millis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1700000000000&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;This helps avoid syntax errors when running the request in Dev Tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Update the License
&lt;/h2&gt;

&lt;p&gt;Run the following request:&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="err"&gt;PUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;_license&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;"license"&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;"uid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"&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;"enterprise"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"issue_date_in_millis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1700000000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expiry_date_in_millis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1800000000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"max_nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"max_resource_units"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"issued_to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your Company"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"issuer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"API"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"signature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REPLACE_WITH_SIGNATURE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start_date_in_millis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1700000000000&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;Expected result:&lt;br&gt;
You should receive an acknowledgment response confirming the license update.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 6: Verify the Update
&lt;/h2&gt;

&lt;p&gt;Run the check again:&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="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;_license&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;The license type is updated&lt;/li&gt;
&lt;li&gt;The expiry date reflects the new license&lt;/li&gt;
&lt;li&gt;The issued organization is correct&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;License updates apply cluster-wide, not per space&lt;/li&gt;
&lt;li&gt;You must have sufficient privileges to update the license&lt;/li&gt;
&lt;li&gt;Changes take effect immediately — no restart required&lt;/li&gt;
&lt;li&gt;Always validate your license before applying it in production&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Updating a license via Dev Tools in Kibana is a quick and reliable method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log in → Select space → Open Dev Tools&lt;/li&gt;
&lt;li&gt;Check current license → Apply new license → Verify&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember:&lt;br&gt;
&lt;strong&gt;One update applies to all namespaces/spaces in the cluster.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>kibana</category>
      <category>elasticsearch</category>
      <category>linux</category>
    </item>
    <item>
      <title>Setting Up ArgoCD on Kubernetes CLuster</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Mon, 16 Mar 2026 22:34:29 +0000</pubDate>
      <link>https://dev.to/durrello/setting-up-argocd-on-minikube-for-a-local-dev-environment-5637</link>
      <guid>https://dev.to/durrello/setting-up-argocd-on-minikube-for-a-local-dev-environment-5637</guid>
      <description>&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; (running)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;minikube&lt;/strong&gt; ≥ v1.30&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;kubectl&lt;/strong&gt; ≥ v1.27&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helm&lt;/strong&gt; ≥ v3.12 &lt;em&gt;(optional but recommended)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;argocd CLI&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1 — Start Minikube
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube start &lt;span class="nt"&gt;--driver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   30s   v1.30.x
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2 — Create the ArgoCD Namespace
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace argocd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3 — Install ArgoCD
&lt;/h3&gt;

&lt;p&gt;Apply the official ArgoCD install manifest:&lt;br&gt;
&lt;/p&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;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify all pods are 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 get pods &lt;span class="nt"&gt;-n&lt;/span&gt; argocd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;NAME                                                READY   STATUS    RESTARTS
argocd-application-controller-0                     1/1     Running   0
argocd-applicationset-controller-xxxxxxxxx-xxxxx    1/1     Running   0
argocd-dex-server-xxxxxxxxx-xxxxx                   1/1     Running   0
argocd-notifications-controller-xxxxxxxxx-xxxxx     1/1     Running   0
argocd-redis-xxxxxxxxx-xxxxx                        1/1     Running   0
argocd-repo-server-xxxxxxxxx-xxxxx                  1/1     Running   0
argocd-server-xxxxxxxxx-xxxxx                       1/1     Running   0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4 — Expose the ArgoCD UI
&lt;/h3&gt;

&lt;p&gt;For local dev, port-forward the ArgoCD API server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/argocd-server &lt;span class="nt"&gt;-n&lt;/span&gt; argocd 8080:443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Keep this terminal open. The UI is now available at &lt;code&gt;https://localhost:8080&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 5 — Install the ArgoCD CLI
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;macOS (Homebrew):&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;brew &lt;span class="nb"&gt;install &lt;/span&gt;argocd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;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;curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
&lt;span class="nb"&gt;sudo install&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 555 argocd-linux-amd64 /usr/local/bin/argocd
&lt;span class="nb"&gt;rm &lt;/span&gt;argocd-linux-amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Windows (Chocolatey):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;choco&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;argocd-cli&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6 — Retrieve the Initial Admin Password
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret argocd-initial-admin-secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{.data.password}"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the output — this is your initial password, user name "admin"&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7 — Log in via CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd login localhost:8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--password&lt;/span&gt; &amp;lt;YOUR_PASSWORD_HERE&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--insecure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;--insecure&lt;/code&gt; skips TLS verification for local dev — do NOT use in production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 8 — Change the Admin Password
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd account update-password &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--current-password&lt;/span&gt; &amp;lt;YOUR_PASSWORD_HERE&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--new-password&lt;/span&gt; &amp;lt;NEW_STRONG_PASSWORD&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 9 — Register Your Cluster (optional for Minikube)
&lt;/h3&gt;

&lt;p&gt;Since we're deploying to the same cluster ArgoCD runs on, register it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd cluster add minikube &lt;span class="nt"&gt;--in-cluster&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List registered clusters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd cluster list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 10 — Deploy Your First Application
&lt;/h3&gt;

&lt;p&gt;This example deploys the official ArgoCD guestbook sample app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd app create guestbook &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/argoproj/argocd-example-apps.git &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--path&lt;/span&gt; guestbook &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dest-server&lt;/span&gt; https://kubernetes.default.svc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dest-namespace&lt;/span&gt; default &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sync-policy&lt;/span&gt; automated &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--auto-prune&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--self-heal&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What each flag does:&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;Flag&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--repo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Git repo containing your manifests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Folder inside the repo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--dest-server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target cluster (in-cluster URL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--dest-namespace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kubernetes namespace to deploy into&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--sync-policy automated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Auto-sync on every Git push&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--auto-prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deletes resources removed from Git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--self-heal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reverts manual kubectl changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 11 — Trigger &amp;amp; Check Sync
&lt;/h3&gt;

&lt;p&gt;Manually sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd app &lt;span class="nb"&gt;sync &lt;/span&gt;guestbook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check app status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd app get guestbook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch live sync status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd app &lt;span class="nb"&gt;wait &lt;/span&gt;guestbook &lt;span class="nt"&gt;--sync&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List all apps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd app list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 12 — Access the ArgoCD Web UI
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;https://localhost:8080&lt;/code&gt; in your browser (accept the self-signed cert warning), log in as &lt;code&gt;admin&lt;/code&gt;, and you'll see the guestbook app fully synced.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus: Use a NodePort Instead of Port-Forward
&lt;/h3&gt;

&lt;p&gt;For a more persistent local setup, patch the service to &lt;code&gt;NodePort&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;kubectl patch svc argocd-server &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec": {"type": "NodePort"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then get the Minikube URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube service argocd-server &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;--url&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bonus: Enable Minikube Addons
&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;# Enable ingress&lt;/span&gt;
minikube addons &lt;span class="nb"&gt;enable &lt;/span&gt;ingress

&lt;span class="c"&gt;# Enable dashboard (useful for debugging)&lt;/span&gt;
minikube addons &lt;span class="nb"&gt;enable &lt;/span&gt;dashboard
minikube dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cleanup
&lt;/h3&gt;

&lt;p&gt;To tear everything down:&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;# Delete the ArgoCD app&lt;/span&gt;
argocd app delete guestbook &lt;span class="nt"&gt;--cascade&lt;/span&gt;

&lt;span class="c"&gt;# Delete ArgoCD namespace&lt;/span&gt;
kubectl delete namespace argocd

&lt;span class="c"&gt;# Stop Minikube&lt;/span&gt;
minikube stop

&lt;span class="c"&gt;# (Optional) Fully delete the cluster&lt;/span&gt;
minikube delete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Quick Reference Cheat Sheet
&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;# Start environment&lt;/span&gt;
minikube start &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8192 &lt;span class="nt"&gt;--driver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker
kubectl apply &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
kubectl port-forward svc/argocd-server &lt;span class="nt"&gt;-n&lt;/span&gt; argocd 8080:443

&lt;span class="c"&gt;# Get password&lt;/span&gt;
kubectl get secret argocd-initial-admin-secret &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{.data.password}"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;

&lt;span class="c"&gt;# Login&lt;/span&gt;
argocd login localhost:8080 &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="nt"&gt;--password&lt;/span&gt; &amp;lt;PASS&amp;gt; &lt;span class="nt"&gt;--insecure&lt;/span&gt;

&lt;span class="c"&gt;# Deploy an app&lt;/span&gt;
argocd app create &amp;lt;APP&amp;gt; &lt;span class="nt"&gt;--repo&lt;/span&gt; &amp;lt;REPO_URL&amp;gt; &lt;span class="nt"&gt;--path&lt;/span&gt; &amp;lt;PATH&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dest-server&lt;/span&gt; https://kubernetes.default.svc &lt;span class="nt"&gt;--dest-namespace&lt;/span&gt; default &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sync-policy&lt;/span&gt; automated &lt;span class="nt"&gt;--auto-prune&lt;/span&gt; &lt;span class="nt"&gt;--self-heal&lt;/span&gt;

&lt;span class="c"&gt;# Sync + status&lt;/span&gt;
argocd app &lt;span class="nb"&gt;sync&lt;/span&gt; &amp;lt;APP&amp;gt;
argocd app get &amp;lt;APP&amp;gt;
argocd app list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>argocd</category>
      <category>gitops</category>
    </item>
    <item>
      <title>PECOS Data Extraction Pipeline - DevOps Documentation</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Fri, 13 Mar 2026 21:55:03 +0000</pubDate>
      <link>https://dev.to/durrello/pecos-data-extraction-pipeline-devops-documentation-385k</link>
      <guid>https://dev.to/durrello/pecos-data-extraction-pipeline-devops-documentation-385k</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;The PECOS Data Extraction Pipeline is an enterprise-grade ETL workflow that extracts, transforms, and loads healthcare provider data from CMS PECOS datasets. The pipeline processes four datasets (Clinicians, Practices, Canonical Providers, Detail Tables) in parallel using PySpark and AWS Glue, orchestrated by AWS Step Functions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/durrello/PECOS-Data-Extraction-Pipeline" rel="noopener noreferrer"&gt;https://github.com/durrello/PECOS-Data-Extraction-Pipeline&lt;/a&gt;&lt;/p&gt;

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

&lt;h3&gt;
  
  
  AWS Architecture (Primary)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    ┌─────────────────────────────────────┐
                    │         AWS Step Functions           │
                    │      State Machine Orchestrator      │
                    └───────────────┬─────────────────────┘
                                    │
                          [ValidateInput]  ← Pass State
                                    │
                    ┌───────────────▼─────────────────────┐
                    │         ExtractParallel              │ ← Parallel State
                    │  ┌──────┐ ┌──────┐ ┌──────┐ ┌────┐ │
                    │  │Clin. │ │Prac. │ │Canon.│ │Det.│ │ ← Task States
                    │  │ ETL  │ │ ETL  │ │ ETL  │ │ETL │ │
                    │  └──────┘ └──────┘ └──────┘ └────┘ │
                    │   ↺ 3 retries per branch            │
                    └───────────────┬─────────────────────┘
                                    │
                          [AllSucceeded?]  ← Choice State
                         /                         \
               [NotifySuccess]              [NotifyFailure]
                     │                              │
             [PipelineSucceed]            [PipelineFail]
              ← Succeed State              ← Fail State
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Alternative Architectures
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Local Python (Development)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Uses ThreadPoolExecutor for parallel execution&lt;/li&gt;
&lt;li&gt;Local file system for data storage&lt;/li&gt;
&lt;li&gt;Console logging for notifications&lt;/li&gt;
&lt;li&gt;No AWS dependencies required&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Docker (EMR Simulation)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Containerized EMR-like environment&lt;/li&gt;
&lt;li&gt;Volume mounts for data persistence&lt;/li&gt;
&lt;li&gt;Isolated Python 3.11 + PySpark 3.5.1 environment&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  AWS Services Used
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Services
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Step Functions&lt;/strong&gt;: Orchestrates the ETL pipeline workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Glue&lt;/strong&gt;: Serverless PySpark ETL jobs (4 parallel jobs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon S3&lt;/strong&gt;: Data lake for input, output, and logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS IAM&lt;/strong&gt;: Fine-grained permissions for services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon SNS&lt;/strong&gt;: Email notifications for pipeline status&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Supporting Services
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS CloudWatch&lt;/strong&gt;: Logging and monitoring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS CloudWatch Logs&lt;/strong&gt;: Detailed execution logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS X-Ray&lt;/strong&gt;: Distributed tracing (optional)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  AWS Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AWS CLI v2 configured with admin permissions&lt;/li&gt;
&lt;li&gt;S3 bucket for data storage (auto-created by bootstrap)&lt;/li&gt;
&lt;li&gt;IAM permissions to create roles, policies, and services&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Local Development Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.11 (required for PySpark compatibility)&lt;/li&gt;
&lt;li&gt;Java 17+ (for Spark runtime)&lt;/li&gt;
&lt;li&gt;Docker (optional, for containerized runs)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deployment Options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: AWS One-Command Bootstrap (Recommended)
&lt;/h3&gt;

&lt;p&gt;The bootstrap script automates the entire AWS deployment:&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;# Basic deployment&lt;/span&gt;
./bootstrap.sh

&lt;span class="c"&gt;# With custom environment and email&lt;/span&gt;
./bootstrap.sh production your-email@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What the bootstrap script does:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates IAM roles with minimal required permissions&lt;/li&gt;
&lt;li&gt;Verifies/creates S3 bucket&lt;/li&gt;
&lt;li&gt;Uploads code, config, and data to S3&lt;/li&gt;
&lt;li&gt;Creates/updates 4 AWS Glue ETL jobs&lt;/li&gt;
&lt;li&gt;Sets up SNS topic and email notifications&lt;/li&gt;
&lt;li&gt;Deploys Step Functions state machine&lt;/li&gt;
&lt;li&gt;Starts initial pipeline execution&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;Execution ARN for monitoring&lt;/li&gt;
&lt;li&gt;Console URLs for Step Functions and Glue&lt;/li&gt;
&lt;li&gt;SNS topic ARN for notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option 2: Manual AWS Deployment
&lt;/h3&gt;

&lt;p&gt;For custom deployments or CI/CD integration:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. IAM Roles Setup
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step Functions execution role&lt;/span&gt;
aws iam create-role &lt;span class="nt"&gt;--role-name&lt;/span&gt; PECOSStepFunctionsRole &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--assume-role-policy-document&lt;/span&gt; &lt;span class="s1"&gt;'{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Principal": {"Service": "states.amazonaws.com"},
      "Action": "sts:AssumeRole"
    }]
  }'&lt;/span&gt;

&lt;span class="c"&gt;# Glue execution role&lt;/span&gt;
aws iam create-role &lt;span class="nt"&gt;--role-name&lt;/span&gt; PECOSGlueRole &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--assume-role-policy-document&lt;/span&gt; &lt;span class="s1"&gt;'{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Principal": {"Service": "glue.amazonaws.com"},
      "Action": "sts:AssumeRole"
    }]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. S3 Bucket Setup
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;BUCKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"durrell-backend-bucket-terraform"&lt;/span&gt;
aws s3 mb s3://&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Upload Artifacts
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Upload Spark jobs&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;spark_jobs/ s3://&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;/spark_jobs/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s2"&gt;"*.pyc"&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s2"&gt;"__pycache__/*"&lt;/span&gt;

&lt;span class="c"&gt;# Upload config&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;config/pipeline_config.yaml s3://&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;/config/

&lt;span class="c"&gt;# Upload input data&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;data/input/ s3://&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;/data/input/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Create Glue Jobs
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example for Clinicians ETL job&lt;/span&gt;
aws glue create-job &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"PECOS-Clinicians-ETL"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::ACCOUNT_ID:role/PECOSGlueRole"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--command&lt;/span&gt; &lt;span class="s2"&gt;"{
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;glueetl&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;ScriptLocation&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;&lt;span class="s2"&gt;/spark_jobs/glue_clinicians.py&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;PythonVersion&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
  }"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--default-arguments&lt;/span&gt; &lt;span class="s2"&gt;"{
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;--config&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;&lt;span class="s2"&gt;/config/pipeline_config.yaml&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;--additional-python-modules&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;pyyaml&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;--enable-continuous-cloudwatch-log&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;--enable-metrics&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
  }"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--glue-version&lt;/span&gt; &lt;span class="s2"&gt;"4.0"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--number-of-workers&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--worker-type&lt;/span&gt; &lt;span class="s2"&gt;"G.1X"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. SNS Notifications Setup
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create topic&lt;/span&gt;
&lt;span class="nv"&gt;TOPIC_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sns create-topic &lt;span class="nt"&gt;--name&lt;/span&gt; pecos-pipeline-notifications &lt;span class="nt"&gt;--query&lt;/span&gt; TopicArn &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Subscribe email&lt;/span&gt;
aws sns subscribe &lt;span class="nt"&gt;--topic-arn&lt;/span&gt; &lt;span class="nv"&gt;$TOPIC_ARN&lt;/span&gt; &lt;span class="nt"&gt;--protocol&lt;/span&gt; email &lt;span class="nt"&gt;--notification-endpoint&lt;/span&gt; your-email@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6. Deploy State Machine
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Substitute variables in ASL&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"s/&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{REGION}/us-east-1/g"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"s/&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{ACCOUNT_ID}/YOUR_ACCOUNT_ID/g"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"s/&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{BUCKET}/&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;&lt;span class="s2"&gt;/g"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    state_machine/pecos_state_machine.asl.json &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; temp_asl.json

&lt;span class="c"&gt;# Create state machine&lt;/span&gt;
aws stepfunctions create-state-machine &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"PECOS-Extraction-Pipeline"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--definition&lt;/span&gt; file://temp_asl.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-arn&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::ACCOUNT_ID:role/PECOSStepFunctionsRole"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option 3: Local Python Development
&lt;/h3&gt;

&lt;p&gt;For development and testing without AWS:&lt;/p&gt;

&lt;h4&gt;
  
  
  Environment Setup
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Python 3.11&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;python@3.11  &lt;span class="c"&gt;# macOS&lt;/span&gt;
python3.11 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Run Pipeline Locally
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Full pipeline&lt;/span&gt;
python state_machine/pipeline_orchestrator.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--input&lt;/span&gt; state_machine/sample_input.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--config&lt;/span&gt; config/pipeline_config.yaml

&lt;span class="c"&gt;# Individual ETL jobs&lt;/span&gt;
python spark_jobs/etl_clinicians.py &lt;span class="nt"&gt;--execution-id&lt;/span&gt; DEV-001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option 4: Docker Development
&lt;/h3&gt;

&lt;p&gt;EMR-like containerized environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build and run&lt;/span&gt;
docker build &lt;span class="nt"&gt;-f&lt;/span&gt; docker/Dockerfile &lt;span class="nt"&gt;-t&lt;/span&gt; pecos-pipeline &lt;span class="nb"&gt;.&lt;/span&gt;
docker-compose up

&lt;span class="c"&gt;# Or run specific command&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/app/data pecos-pipeline &lt;span class="se"&gt;\&lt;/span&gt;
  python state_machine/pipeline_orchestrator.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration Management
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pipeline Configuration (pipeline_config.yaml)
&lt;/h3&gt;

&lt;p&gt;The configuration is environment-aware and supports both local and AWS deployments:&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;pipeline&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PECOS-Extraction-Pipeline"&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local"&lt;/span&gt;  &lt;span class="c1"&gt;# local | dev | staging | prod&lt;/span&gt;
  &lt;span class="na"&gt;execution_id_prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PECOS"&lt;/span&gt;

&lt;span class="c1"&gt;# S3 paths (AWS) vs local paths&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;base_input_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://bucket/data/input"&lt;/span&gt;   &lt;span class="c1"&gt;# AWS&lt;/span&gt;
  &lt;span class="na"&gt;base_output_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://bucket/data/output"&lt;/span&gt; &lt;span class="c1"&gt;# AWS&lt;/span&gt;

&lt;span class="na"&gt;local_paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;base_input_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/input"&lt;/span&gt;    &lt;span class="c1"&gt;# Local&lt;/span&gt;
  &lt;span class="na"&gt;base_output_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/output"&lt;/span&gt;  &lt;span class="c1"&gt;# Local&lt;/span&gt;

&lt;span class="c1"&gt;# Dataset-specific configs&lt;/span&gt;
&lt;span class="na"&gt;datasets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;clinicians&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;input_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clinicians.csv"&lt;/span&gt;
    &lt;span class="na"&gt;partition_by&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;state"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;primary_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;npi"&lt;/span&gt;
    &lt;span class="na"&gt;required_columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;...&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Environment Variables
&lt;/h3&gt;

&lt;p&gt;Override configuration at runtime:&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;export &lt;/span&gt;&lt;span class="nv"&gt;PECOS_BASE_INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;data/input
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PECOS_BASE_OUTPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;data/output
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PECOS_LOG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;logs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  AWS Monitoring
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Step Functions Console
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Real-time execution visualization&lt;/li&gt;
&lt;li&gt;State transition timing&lt;/li&gt;
&lt;li&gt;Error details and stack traces&lt;/li&gt;
&lt;li&gt;Execution history and re-runs&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Glue Console
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Job run status and duration&lt;/li&gt;
&lt;li&gt;Resource utilization (DPUs)&lt;/li&gt;
&lt;li&gt;Error logs and troubleshooting&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  CloudWatch Logs
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Structured logs from all components&lt;/li&gt;
&lt;li&gt;Log groups:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/aws/states/PECOS-Extraction-Pipeline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/aws-glue/jobs/logs-v2/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  CloudWatch Metrics
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Step Functions: Execution metrics&lt;/li&gt;
&lt;li&gt;Glue: Job performance metrics&lt;/li&gt;
&lt;li&gt;S3: Storage and access metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Local Monitoring
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Console Logging
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Structured JSON logs with timestamps&lt;/li&gt;
&lt;li&gt;Execution summaries in &lt;code&gt;logs/{execution_id}_summary.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dataset-specific logs in &lt;code&gt;logs/{execution_id}_{dataset}.log&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  File System Monitoring
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Output Parquet files in output&lt;/li&gt;
&lt;li&gt;Partitioned by state/status&lt;/li&gt;
&lt;li&gt;Snappy-compressed for efficiency&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pipeline Execution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Starting Executions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  AWS Step Functions
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Prepare input&lt;/span&gt;
&lt;span class="nv"&gt;EXECUTION_INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{
  "trigger_source": "manual",
  "run_date": "'&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m-%d&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s1"&gt;'",
  "environment": "production",
  "config_s3_uri": "s3://bucket/config/pipeline_config.yaml",
  "input_s3_prefix": "s3://bucket/data/input/",
  "output_s3_prefix": "s3://bucket/data/output/"
}'&lt;/span&gt;

&lt;span class="c"&gt;# Start execution&lt;/span&gt;
&lt;span class="nv"&gt;EXECUTION_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws stepfunctions start-execution &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--state-machine-arn&lt;/span&gt; &lt;span class="nv"&gt;$STATE_MACHINE_ARN&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--input&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXECUTION_INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; executionArn &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Local Execution
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python state_machine/pipeline_orchestrator.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--input&lt;/span&gt; state_machine/sample_input.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--config&lt;/span&gt; config/pipeline_config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Execution States
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Retry Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ValidateInput&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass&lt;/td&gt;
&lt;td&gt;Enrich input with metadata&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExtractParallel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parallel&lt;/td&gt;
&lt;td&gt;Run 4 ETL jobs concurrently&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExtractCliniciansTask&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Task&lt;/td&gt;
&lt;td&gt;Clinicians ETL&lt;/td&gt;
&lt;td&gt;3 retries, 2x backoff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExtractPracticesTask&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Task&lt;/td&gt;
&lt;td&gt;Practices ETL&lt;/td&gt;
&lt;td&gt;3 retries, 2x backoff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExtractCanonicalProvidersTask&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Task&lt;/td&gt;
&lt;td&gt;Canonical Providers ETL&lt;/td&gt;
&lt;td&gt;3 retries, 2x backoff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExtractDetailTablesTask&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Task&lt;/td&gt;
&lt;td&gt;Detail Tables ETL&lt;/td&gt;
&lt;td&gt;3 retries, 2x backoff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AllSucceeded&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Choice&lt;/td&gt;
&lt;td&gt;Route based on results&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NotifySuccess&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Task&lt;/td&gt;
&lt;td&gt;Send success notification&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NotifyFailure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Task&lt;/td&gt;
&lt;td&gt;Send failure notification&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PipelineSucceed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Succeed&lt;/td&gt;
&lt;td&gt;Terminal success&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PipelineFail&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fail&lt;/td&gt;
&lt;td&gt;Terminal failure&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Data Processing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ETL Transformations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Clinicians Dataset
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Filter active providers (&lt;code&gt;status = A&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Zero-pad NPI to 10 digits&lt;/li&gt;
&lt;li&gt;Derive &lt;code&gt;credential_group&lt;/code&gt; (Physician/Mid-Level/Nursing/Other)&lt;/li&gt;
&lt;li&gt;Compute &lt;code&gt;years_enrolled&lt;/code&gt; from enrollment date&lt;/li&gt;
&lt;li&gt;Data quality flag: &lt;code&gt;dq_has_specialty&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Deduplicate on &lt;code&gt;(npi, state)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Practices Dataset
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Filter active practices&lt;/li&gt;
&lt;li&gt;Derive &lt;code&gt;practice_size_category&lt;/code&gt; (Solo/Small/Medium/Large/Enterprise)&lt;/li&gt;
&lt;li&gt;Map state → US Census region&lt;/li&gt;
&lt;li&gt;Flag multi-specialty groups&lt;/li&gt;
&lt;li&gt;Deduplicate on &lt;code&gt;practice_id&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Canonical Providers Dataset
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Build golden record using window functions&lt;/li&gt;
&lt;li&gt;Compute &lt;code&gt;participation_score&lt;/code&gt; (0-3: Medicare + Medicaid + Telehealth)&lt;/li&gt;
&lt;li&gt;Assign &lt;code&gt;participation_tier&lt;/code&gt; (Full/Partial/Limited/None)&lt;/li&gt;
&lt;li&gt;Build &lt;code&gt;display_name&lt;/code&gt; for individuals and organizations&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Detail Tables Dataset
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Parse and classify enrollment events&lt;/li&gt;
&lt;li&gt;Compute &lt;code&gt;enrollment_duration_days&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Decode &lt;code&gt;reason_code&lt;/code&gt; to human-readable descriptions&lt;/li&gt;
&lt;li&gt;Classify &lt;code&gt;termination_type&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Compute window-based &lt;code&gt;group_reassignment_rate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Flag &lt;code&gt;is_recent_enrollment&lt;/code&gt; (&amp;lt; 180 days) and &lt;code&gt;is_stale_record&lt;/code&gt; (&amp;gt; 5 years)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Output Format
&lt;/h3&gt;

&lt;p&gt;All datasets output &lt;strong&gt;Snappy-compressed Parquet&lt;/strong&gt; with metadata columns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;_pipeline_execution_id&lt;/code&gt;: Unique run identifier&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_pipeline_ingested_at&lt;/code&gt;: UTC ingestion timestamp&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_pipeline_source_file&lt;/code&gt;: Source file path&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Partitioning strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clinicians/Practices/Canonical: &lt;code&gt;state=XX/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Detail Tables: &lt;code&gt;state=XX/status=Y/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Notifications
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AWS SNS Notifications
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Email notifications for success/failure&lt;/li&gt;
&lt;li&gt;Structured JSON payload with execution details&lt;/li&gt;
&lt;li&gt;Configurable recipients&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Local Notifications
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Console output with execution summary&lt;/li&gt;
&lt;li&gt;JSON-formatted logs&lt;/li&gt;
&lt;li&gt;File-based execution summaries&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;h3&gt;
  
  
  IAM Permissions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Principle of Least Privilege
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Step Functions role: Only Glue start/run, SNS publish, CloudWatch logs&lt;/li&gt;
&lt;li&gt;Glue role: Scoped S3 access (only pipeline bucket), Glue service role&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  S3 Bucket Policies
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Separate permissions for input, output, and logs&lt;/li&gt;
&lt;li&gt;No wildcard permissions&lt;/li&gt;
&lt;li&gt;Encryption at rest (SSE-S3)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Network Security
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;All services communicate within AWS network&lt;/li&gt;
&lt;li&gt;No public endpoints exposed&lt;/li&gt;
&lt;li&gt;VPC deployment possible for enhanced security&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cost Optimization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AWS Glue
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;G.1X worker type (cost-effective for CPU-bound workloads)&lt;/li&gt;
&lt;li&gt;2 workers per job (parallel processing within job)&lt;/li&gt;
&lt;li&gt;Auto-scaling disabled (predictable costs)&lt;/li&gt;
&lt;li&gt;Timeout: 60 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step Functions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Standard workflow (cost-effective for ETL orchestration)&lt;/li&gt;
&lt;li&gt;No Express workflows needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  S3 Storage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Intelligent tiering for output data&lt;/li&gt;
&lt;li&gt;Lifecycle policies for logs&lt;/li&gt;
&lt;li&gt;Snappy compression reduces storage costs&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  Common Issues
&lt;/h3&gt;

&lt;h4&gt;
  
  
  SNS Email Not Received
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Confirm subscription in email&lt;/li&gt;
&lt;li&gt;Check SNS topic permissions&lt;/li&gt;
&lt;li&gt;Verify email address format&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Glue Job Failures
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Check CloudWatch logs: &lt;code&gt;/aws-glue/jobs/logs-v2/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Verify S3 permissions&lt;/li&gt;
&lt;li&gt;Check PySpark version compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step Functions Timeouts
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Increase Glue job timeout&lt;/li&gt;
&lt;li&gt;Optimize Spark configurations&lt;/li&gt;
&lt;li&gt;Check for data skew&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Local Development Issues
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Ensure Python 3.11 (PySpark requirement)&lt;/li&gt;
&lt;li&gt;Check Java version (17+)&lt;/li&gt;
&lt;li&gt;Verify virtual environment activation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Debugging Commands
&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 execution status&lt;/span&gt;
aws stepfunctions describe-execution &lt;span class="nt"&gt;--execution-arn&lt;/span&gt; &lt;span class="nv"&gt;$EXECUTION_ARN&lt;/span&gt;

&lt;span class="c"&gt;# View Glue job logs&lt;/span&gt;
aws logs &lt;span class="nb"&gt;tail&lt;/span&gt; /aws-glue/jobs/logs-v2/ &lt;span class="nt"&gt;--follow&lt;/span&gt;

&lt;span class="c"&gt;# List output files&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;ls &lt;/span&gt;s3://bucket/data/output/ &lt;span class="nt"&gt;--recursive&lt;/span&gt;

&lt;span class="c"&gt;# Check S3 permissions&lt;/span&gt;
aws s3api get-bucket-policy &lt;span class="nt"&gt;--bucket&lt;/span&gt; bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AWS Resource Cleanup
&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;# Automated teardown&lt;/span&gt;
./teardown.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Step Functions state machine&lt;/li&gt;
&lt;li&gt;4 AWS Glue ETL jobs&lt;/li&gt;
&lt;li&gt;SNS topic and subscriptions&lt;/li&gt;
&lt;li&gt;S3 bucket and all contents&lt;/li&gt;
&lt;li&gt;IAM roles and policies&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Local Cleanup
&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;# Remove outputs and logs&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; data/output/&lt;span class="k"&gt;*&lt;/span&gt; logs/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Remove virtual environment&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; .venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Tuning
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Spark Configurations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spark&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spark.sql.shuffle.partitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4"&lt;/span&gt;
    &lt;span class="na"&gt;spark.default.parallelism&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4"&lt;/span&gt;
    &lt;span class="na"&gt;spark.sql.adaptive.enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
    &lt;span class="na"&gt;spark.sql.parquet.compression.codec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;snappy"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Glue Job Sizing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;G.1X workers: 1 DPU, 16GB RAM, 4 vCPU&lt;/li&gt;
&lt;li&gt;Scale workers based on data volume&lt;/li&gt;
&lt;li&gt;Monitor DPU utilization in Glue console&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Parallel Processing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;4 ETL jobs run concurrently&lt;/li&gt;
&lt;li&gt;No inter-job dependencies&lt;/li&gt;
&lt;li&gt;Optimal for I/O bound workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Backup and Recovery
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Data Backup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;S3 versioning enabled on bucket&lt;/li&gt;
&lt;li&gt;Cross-region replication for critical data&lt;/li&gt;
&lt;li&gt;Regular snapshots of configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pipeline Recovery
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Step Functions supports execution re-runs&lt;/li&gt;
&lt;li&gt;Idempotent ETL jobs (overwrite mode)&lt;/li&gt;
&lt;li&gt;Partial failure handling (continue with successful jobs)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Compliance and Governance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Data Governance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Structured logging with execution IDs&lt;/li&gt;
&lt;li&gt;Data lineage tracking&lt;/li&gt;
&lt;li&gt;Audit trails in CloudWatch&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Healthcare Compliance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;PHI data handling considerations&lt;/li&gt;
&lt;li&gt;Encryption at rest and in transit&lt;/li&gt;
&lt;li&gt;Access logging and monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future Enhancements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Potential Improvements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Event-driven triggers (S3 event notifications)&lt;/li&gt;
&lt;li&gt;Data quality frameworks (Great Expectations)&lt;/li&gt;
&lt;li&gt;Advanced monitoring (custom CloudWatch dashboards)&lt;/li&gt;
&lt;li&gt;Multi-region deployment&lt;/li&gt;
&lt;li&gt;Blue/green deployment strategy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scaling Considerations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;EMR migration for large datasets&lt;/li&gt;
&lt;li&gt;Kubernetes deployment option&lt;/li&gt;
&lt;li&gt;Serverless architecture evolution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This documentation provides comprehensive DevOps guidance for deploying and managing the PECOS Data Extraction Pipeline across AWS, local, and containerized environments. The pipeline demonstrates enterprise-grade ETL patterns with robust error handling, monitoring, and cost optimization.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>aws</category>
      <category>dataengineering</category>
      <category>devops</category>
    </item>
    <item>
      <title>LocalTunnel Server Setup Documentation</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Fri, 05 Dec 2025 00:44:40 +0000</pubDate>
      <link>https://dev.to/durrello/localtunnel-server-setup-documentation-370k</link>
      <guid>https://dev.to/durrello/localtunnel-server-setup-documentation-370k</guid>
      <description>&lt;h1&gt;
  
  
  LocalTunnel Server Setup Documentation
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Files
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 &lt;code&gt;docker-compose.yml&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;lt-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;defunctzombie/localtunnel-server:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lt-server&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;ports&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;3000:3000"&lt;/span&gt;          
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;localtunnel-net&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bin/server --port 3000 --host 0.0.0.0&lt;/span&gt; 

  &lt;span class="na"&gt;localtunnel-nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localtunnel-nginx&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lt-server&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;ports&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;8081:80"&lt;/span&gt;            
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./nginx.conf:/etc/nginx/nginx.conf:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./site.conf:/etc/nginx/conf.d/default.conf:ro&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;localtunnel-net&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;localtunnel-net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  1.2 &lt;code&gt;nginx.conf&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;events&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;worker_connections&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/conf.d/*.conf&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;
  
  
  1.3 &lt;code&gt;site.conf&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://lt-server:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;"upgrade"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&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;
  
  
  1.4 Running LocalTunnel Client Command
&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;nohup &lt;/span&gt;npx localtunnel &lt;span class="nt"&gt;--port&lt;/span&gt; 3000 &lt;span class="nt"&gt;--host&lt;/span&gt; http://34.68.37.115:8081 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tunnel.log 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This runs LocalTunnel in the background and logs output to &lt;code&gt;tunnel.log&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Example log output:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nohup: ignoring input
your url is: http://angry-liger-20.34.68.37.115
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Installation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Docker &amp;amp; Docker Compose installed.&lt;/li&gt;
&lt;li&gt;Node.js and &lt;code&gt;npx&lt;/code&gt; installed (for running the LocalTunnel client).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.2 Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Clone or copy the configuration files into a directory:
&lt;/li&gt;
&lt;/ol&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; ~/localtunnel
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/localtunnel
&lt;span class="c"&gt;# Add docker-compose.yml, nginx.conf, site.conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Start the LocalTunnel server and Nginx reverse proxy:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lt-server&lt;/code&gt; listens internally on port &lt;code&gt;3000&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;localtunnel-nginx&lt;/code&gt; exposes port &lt;code&gt;8081&lt;/code&gt; externally.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Verify containers are running:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Start LocalTunnel Client
&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;nohup &lt;/span&gt;npx localtunnel &lt;span class="nt"&gt;--port&lt;/span&gt; 3000 &lt;span class="nt"&gt;--host&lt;/span&gt; http://&amp;lt;your-server-ip&amp;gt;:8081 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tunnel.log 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;&amp;lt;your-server-ip&amp;gt;&lt;/code&gt; with your public server IP or domain.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tunnel.log&lt;/code&gt; stores the output including your public URL.&lt;/li&gt;
&lt;li&gt;Example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your url is: http://angry-liger-20.34.68.37.115
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 Check Logs
&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;cat &lt;/span&gt;tunnel.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Confirms that the tunnel is running and gives the public URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.3 Stop Tunnel
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Find the process and kill it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;jobs
kill&lt;/span&gt; %1   &lt;span class="c"&gt;# or the relevant job number&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>devops</category>
      <category>tooling</category>
      <category>tutorial</category>
      <category>docker</category>
    </item>
    <item>
      <title>Integrating Bitbucket with ArgoCD</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Wed, 15 Oct 2025 00:39:30 +0000</pubDate>
      <link>https://dev.to/durrello/integrating-bitbucket-with-argocd-114a</link>
      <guid>https://dev.to/durrello/integrating-bitbucket-with-argocd-114a</guid>
      <description>&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;This guide explains how to set up and connect Bitbucket repositories with ArgoCD for GitOps-powered continuous deployment in Kubernetes. ArgoCD monitors your Bitbucket Git repository for changes and ensures your Kubernetes cluster stays synchronized with the desired application state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes cluster with ArgoCD installed and running&lt;/li&gt;
&lt;li&gt;Bitbucket repository containing your application manifests or Helm charts&lt;/li&gt;
&lt;li&gt;Access to Bitbucket account with rights to generate API tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Bitbucket Scoped API Token
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log in to Bitbucket at &lt;a href="https://bitbucket.org" rel="noopener noreferrer"&gt;bitbucket.org&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Personal Settings&lt;/strong&gt; → &lt;strong&gt;Atlassian Account Settings&lt;/strong&gt; → &lt;strong&gt;Security&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Create and manage API tokens&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create API token&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enter a name (e.g., "ArgoCD access token").&lt;/li&gt;
&lt;li&gt;Select an expiration date if desired.&lt;/li&gt;
&lt;li&gt;Choose scopes:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;repository:read&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;repository:write&lt;/code&gt; (if write operations needed)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;account:read&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create token&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Copy the token immediately; you won't be able to retrieve it later.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Add Bitbucket Repository to ArgoCD
&lt;/h3&gt;

&lt;p&gt;In Bitbucket, open the target repository.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click the Clone button.&lt;/li&gt;
&lt;li&gt;Copy the HTTPS URL exactly as it appears (e.g., &lt;a href="https://bitbucket.org/workspace/repository.git" rel="noopener noreferrer"&gt;https://bitbucket.org/workspace/repository.git&lt;/a&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3: Add Bitbucket Repo to ArgoCD
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;You can add the repository through ArgoCD UI or CLI by using:&lt;/li&gt;
&lt;li&gt;Repository URL: The clone URL copied from Bitbucket.&lt;/li&gt;
&lt;li&gt;Username: Your Bitbucket username&lt;/li&gt;
&lt;li&gt;Password: Your app password or API token.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 4: Create and Sync ArgoCD Application
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In ArgoCD UI or CLI, create a new application specifying:

&lt;ul&gt;
&lt;li&gt;Source repo URL (your Bitbucket repo)&lt;/li&gt;
&lt;li&gt;Target revision (branch, tag, or commit)&lt;/li&gt;
&lt;li&gt;Path within repo where manifests are located&lt;/li&gt;
&lt;li&gt;Kubernetes cluster and namespace for deployment&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sync the application to deploy manifests from Bitbucket.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Troubleshooting Tips
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If connection status fails, verify:&lt;/li&gt;
&lt;li&gt;Correct repo URL format (HTTPS, no embedded credentials).&lt;/li&gt;
&lt;li&gt;Use username not just email, check to make sure.&lt;/li&gt;
&lt;li&gt;Network access from ArgoCD to Bitbucket.&lt;/li&gt;
&lt;li&gt;ArgoCD version is 2.5+ for proper API token support.&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;argocd-repo-server&lt;/code&gt; logs for specific error messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://argo-cd.readthedocs.io/en/stable/user-guide/private-repositories/" rel="noopener noreferrer"&gt;ArgoCD Official Docs - Private Repositories&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.atlassian.com/bitbucket-cloud/docs/create-an-app-password/" rel="noopener noreferrer"&gt;Bitbucket API Tokens and Permissions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cloudthat.com/resources/blog/integrating-bitbucket-repositories-with-argo-cd" rel="noopener noreferrer"&gt;Connecting Bitbucket to ArgoCD (CloudThat)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/hooks" rel="noopener noreferrer"&gt;Bitbucket Webhook Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide provides the comprehensive steps and best practices to integrate Bitbucket with ArgoCD for secure, automated Kubernetes deployments managing your applications declaratively through GitOps. If you follow these steps, you will achieve a seamless CI/CD workflow between Bitbucket and your Kubernetes cluster managed by ArgoCD.&lt;/p&gt;

</description>
      <category>argocd</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>bitbucket</category>
    </item>
    <item>
      <title>Configure ArgoCD Ingress on GCP with Custom Domain and Auto TLS via cert-manager</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Tue, 14 Oct 2025 19:31:16 +0000</pubDate>
      <link>https://dev.to/durrello/configure-argocd-ingress-on-gcp-with-custom-domain-and-auto-tls-via-cert-manager-4h3l</link>
      <guid>https://dev.to/durrello/configure-argocd-ingress-on-gcp-with-custom-domain-and-auto-tls-via-cert-manager-4h3l</guid>
      <description>&lt;p&gt;Managing secure access to ArgoCD's web UI on Google Cloud Platform (GCP) can be streamlined with Kubernetes Ingress and cert-manager. This guide walks you through configuring ArgoCD to be accessible via a custom domain with HTTPS, automatically issuing and renewing TLS certificates from Let's Encrypt.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;ArgoCD installed in a Kubernetes cluster on GCP&lt;/li&gt;
&lt;li&gt;nginx ingress controller installed and running with SSL passthrough enabled&lt;/li&gt;
&lt;li&gt;kubectl configured to manage your cluster&lt;/li&gt;
&lt;li&gt;A DNS record pointing your domain (e.g., &lt;code&gt;argocd-example.com&lt;/code&gt;) to the ingress controller's external IP&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Install cert-manager and ingress-nginx
&lt;/h2&gt;

&lt;p&gt;cert-manager automates certificate management on Kubernetes.&lt;br&gt;
&lt;/p&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/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;available &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3m deployment/cert-manager &lt;span class="nt"&gt;-n&lt;/span&gt; cert-manager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the official NGINX Ingress Controller:&lt;/p&gt;

&lt;p&gt;Using Helm (recommended)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then verify:&lt;br&gt;
&lt;code&gt;kubectl -n ingress-nginx get svc&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2: Create a ClusterIssuer for Let's Encrypt
&lt;/h2&gt;

&lt;p&gt;Create a file &lt;code&gt;cluster-issuer.yaml&lt;/code&gt;:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cert-manager.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIssuer&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;letsencrypt-prod&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;acme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://acme-v02.api.letsencrypt.org/directory&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-email@example.com&lt;/span&gt;   &lt;span class="c1"&gt;# Change this to your email&lt;/span&gt;
    &lt;span class="na"&gt;privateKeySecretRef&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;letsencrypt-prod&lt;/span&gt;
    &lt;span class="na"&gt;solvers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http01&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply it:&lt;br&gt;
&lt;/p&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; cluster-issuer.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Patch ArgoCD Server Service to Use HTTPS Port Name
&lt;/h2&gt;

&lt;p&gt;Ensure the &lt;code&gt;argocd-server&lt;/code&gt; service exposes port 443 with name &lt;code&gt;https&lt;/code&gt; pointing to &lt;code&gt;8080&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;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; argocd patch svc argocd-server &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec": {"ports": [{"name": "https", "port": 443, "targetPort": 8080}]}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Create the Ingress Definition with cert-manager Annotation
&lt;/h2&gt;

&lt;p&gt;Save the following as &lt;code&gt;argocd-ingress.yaml&lt;/code&gt;:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;argocd-server-ingress&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kubernetes.io/ingress.class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
    &lt;span class="na"&gt;cert-manager.io/cluster-issuer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;letsencrypt-prod&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/backend-protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HTTPS"&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd-example.com&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&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;argocd-server&lt;/span&gt;
            &lt;span class="na"&gt;port&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;https&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;argocd-example.com&lt;/span&gt;
    &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd-server-tls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the ingress resource:&lt;br&gt;
&lt;/p&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; argocd-ingress.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Update ArgoCD Configuration Map
&lt;/h2&gt;

&lt;p&gt;Update ArgoCD to recognize the new URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl patch cm argocd-cm &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;--type&lt;/span&gt; merge &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"data":{"url":"https://argocd-example.com"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Restart ArgoCD Server Deployment
&lt;/h2&gt;

&lt;p&gt;Reload ArgoCD server to apply changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl rollout restart deployment argocd-server &lt;span class="nt"&gt;-n&lt;/span&gt; argocd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Update DNS Records
&lt;/h2&gt;

&lt;p&gt;Point &lt;code&gt;argocd-example.com&lt;/code&gt; DNS A or CNAME record to your ingress controller's external IP, retrievable via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; ingress-nginx get svc ingress-nginx-controller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Check the status of the issued certificate:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  kubectl describe certificate &lt;span class="nt"&gt;-n&lt;/span&gt; argocd argocd-server-tls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Access ArgoCD at &lt;code&gt;https://argocd-example.com&lt;/code&gt; in a browser; it should load securely with a valid Let’s Encrypt TLS certificate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get the ArgoCD admin password in Kubernetes, use the following command which retrieves the initial admin password stored as a Kubernetes secret and decodes it from base64:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command fetches the password from the secret named &lt;code&gt;argocd-initial-admin-secret&lt;/code&gt; in the &lt;code&gt;argocd&lt;/code&gt; namespace, which is the default namespace where ArgoCD is installed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Details:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The default username is &lt;code&gt;admin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you want to reset the admin password, you can nullify the current password in the &lt;code&gt;argocd-secret&lt;/code&gt; and restart the ArgoCD server pods to revert to the initial password from the secret.&lt;/li&gt;
&lt;li&gt;To reset, run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl -n argocd patch secret argocd-secret -p '{"data": {"admin.password": null, "admin.passwordMtime": null}}'
kubectl delete pods -n argocd -l app.kubernetes.io/name=argocd-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then retrieve the initial password again with the first command.&lt;/p&gt;

&lt;p&gt;By combining Kubernetes Ingress, cert-manager, and ArgoCD, you can securely expose your Kubernetes GitOps dashboard with fully automated certificate management on GCP.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>argocd</category>
      <category>learning</category>
    </item>
    <item>
      <title>How to Host a Static Website on AWS S3 with GitHub and Configure CloudFront, ACM, and Route 53</title>
      <dc:creator>Durrell  Gemuh</dc:creator>
      <pubDate>Wed, 01 Oct 2025 09:27:05 +0000</pubDate>
      <link>https://dev.to/durrello/how-to-host-a-static-website-on-aws-s3-with-github-and-configure-cloudfront-acm-and-route-53-2hik</link>
      <guid>https://dev.to/durrello/how-to-host-a-static-website-on-aws-s3-with-github-and-configure-cloudfront-acm-and-route-53-2hik</guid>
      <description>&lt;p&gt;This guide will walk you through hosting a static website on AWS S3 using your code from &lt;a href="https://github.com/DevOps-Playground-CM/playground-portfolio" rel="noopener noreferrer"&gt;GitHub(my case)&lt;/a&gt; or any other place you have it deployed, and securing it with CloudFront, ACM, and Route 53. Each step is presented clearly to help you deploy your site with ease.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Prepare Your Static Website Code on GitHub
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Push your static website files (HTML, CSS, JavaScript) to a &lt;a href="https://github.com/DevOps-Playground-CM/playground-portfolio" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Make sure your main entry file is named &lt;code&gt;index.html&lt;/code&gt; or as appropriate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Create an S3 Bucket and Upload Your Website Files
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Log in to the AWS Management Console and open the S3 service.&lt;/li&gt;
&lt;li&gt;Create a new bucket named after your website domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Choose a region close to your audience.&lt;/li&gt;
&lt;li&gt;Disable “Block all public access” temporarily for this bucket.&lt;/li&gt;
&lt;li&gt;Upload your website files from GitHub (download/archive or through CI/CD pipeline).&lt;/li&gt;
&lt;li&gt;Go to the bucket Properties → Static website hosting.&lt;/li&gt;
&lt;li&gt;Enable static website hosting, set the index document (&lt;code&gt;index.html&lt;/code&gt;), and error document (&lt;code&gt;error.html&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Save your changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 3: Set Public Access Bucket Policy
&lt;/h2&gt;

&lt;p&gt;To allow public access to your website files:&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;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PublicReadGetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="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="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::your-bucket-name/*"&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;ul&gt;
&lt;li&gt;Replace &lt;code&gt;"your-bucket-name"&lt;/code&gt; with your actual bucket name.&lt;/li&gt;
&lt;li&gt;Apply this policy in the Permissions tab under Bucket Policy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Test Your S3 Website Endpoint
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Go to the Static website hosting section of your bucket and copy the Endpoint URL.&lt;/li&gt;
&lt;li&gt;Open the URL in your browser, and confirm your static site loads correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5: Configure CloudFront, ACM, and Route 53 (To be covered next)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Request an SSL certificate from AWS Certificate Manager (ACM) in &lt;code&gt;us-east-1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Validate your domain using DNS through Route 53.&lt;/li&gt;
&lt;li&gt;Create a CloudFront distribution with your S3 bucket as the origin.&lt;/li&gt;
&lt;li&gt;Use HTTPS and attach the ACM certificate.&lt;/li&gt;
&lt;li&gt;Point your domain to CloudFront via Route 53 alias records.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Following these steps, you can host your static website on AWS with code managed on GitHub and prepare for a secure, performant setup using CloudFront, ACM, and Route 53.&lt;/p&gt;

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