<?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: CodeHiRise</title>
    <description>The latest articles on DEV Community by CodeHiRise (@codehirise).</description>
    <link>https://dev.to/codehirise</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%2F1156987%2F29d91f5d-fa0a-4f64-abdd-a432eab04340.png</url>
      <title>DEV Community: CodeHiRise</title>
      <link>https://dev.to/codehirise</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codehirise"/>
    <language>en</language>
    <item>
      <title>Going Production on Cloud: Infrastructure Best Practices Every Engineer Should Know (Part 1)</title>
      <dc:creator>CodeHiRise</dc:creator>
      <pubDate>Thu, 07 May 2026 18:40:38 +0000</pubDate>
      <link>https://dev.to/codehirise/going-production-on-cloud-infrastructure-best-practices-every-engineer-should-know-part-1-4fen</link>
      <guid>https://dev.to/codehirise/going-production-on-cloud-infrastructure-best-practices-every-engineer-should-know-part-1-4fen</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Hi everyone,&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;So you have been tinkering on a new project for a while, the demos look great, and now it is time to ship to real customers. It is an exciting moment, but also one where small oversights can turn into expensive incidents at 2 AM.&lt;/p&gt;

&lt;p&gt;This two-part series covers the practical checklist I wish every team went through before flipping the switch to production. Most recommendations are cloud-agnostic, although a few are AWS-specific. The underlying principles apply equally to GCP, Azure, or any other cloud provider.&lt;/p&gt;

&lt;p&gt;In this first part we focus on the &lt;strong&gt;infrastructure foundations&lt;/strong&gt;: environments, networking, compute, configuration, and deployment strategy. The second part will cover security, monitoring, logging, and operational work.&lt;/p&gt;



&lt;p&gt;Let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Always Maintain a Production-Like Non-Production Environment
&lt;/h2&gt;

&lt;p&gt;This sounds obvious, but it is one of the most commonly skipped fundamentals. Always maintain at least one non-production environment (usually &lt;code&gt;staging&lt;/code&gt; or &lt;code&gt;qa&lt;/code&gt;) where releases are tested before they reach production.&lt;/p&gt;

&lt;p&gt;The catch is that this environment should genuinely &lt;strong&gt;mirror&lt;/strong&gt; production. That means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The same services and dependencies.&lt;/li&gt;
&lt;li&gt;The same versions of every runtime, library, and managed service.&lt;/li&gt;
&lt;li&gt;The same network topology (VPCs, subnets, security groups).&lt;/li&gt;
&lt;li&gt;The same authentication and IAM patterns.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your staging uses PostgreSQL 15 but production runs PostgreSQL 14, or staging talks to a different message broker version, your "tested" release can still fail in production. Smaller instance sizes are perfectly fine for cost reasons; mismatched versions are not.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
A scaled-down staging environment is usually enough. You don't need the same number of replicas, just the same configurations.&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Segregate Your Network with Public and Private Subnets
&lt;/h2&gt;

&lt;p&gt;A common mistake on new cloud projects is putting everything in a single public subnet because it "just works." It works, until it doesn't.&lt;/p&gt;

&lt;p&gt;The standard pattern is straightforward.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;public subnet&lt;/strong&gt; holds resources that must be reachable from the internet, such as load balancers, NAT gateways, and bastion hosts. A &lt;strong&gt;private subnet&lt;/strong&gt; holds everything else: application servers, databases, internal services.&lt;/p&gt;

&lt;p&gt;Resources in the private subnet have no direct route from the internet, which dramatically reduces the attack surface. If an attacker cannot reach your database, it gets much harder them to exploit it.&lt;/p&gt;

&lt;p&gt;You might argue that exposing a database publicly is still safe when access is tightly controlled through security groups. While that is technically true, security often fails at the edges of human error. A single misconfigured security group rule can unintentionally expose the database to the internet.&lt;/p&gt;

&lt;p&gt;Keeping the database in a private subnet adds an additional layer of protection. Even if a security group is accidentally misconfigured, the database still remains unreachable from the outside world. This design reduces the likelihood of accidental exposure and makes costly mistakes much harder to make.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
   │
   ▼
[Load Balancer]   ── public subnet
   │
   ▼
[Application]     ── private subnet
   │
   ▼
[Database]        ── private subnet (often a separate one)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For outbound access from private subnets (calling third-party APIs, pulling container images, downloading OS updates), use a &lt;strong&gt;NAT gateway&lt;/strong&gt; placed in the public subnet.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
For high availability, deploy a NAT gateway in each Availability Zone you use. A single NAT in one AZ becomes a cross-AZ single point of failure.&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Control Your Ingress and Egress IP Addresses
&lt;/h2&gt;

&lt;p&gt;Once your application is in production, you will inevitably need to integrate with third parties. Many of them, particularly payment providers, require you to provide a fixed list of IP addresses to whitelist on their firewall. If your egress IPs change unpredictably, those integrations break.&lt;/p&gt;

&lt;p&gt;This is where a &lt;strong&gt;NAT gateway with a static Elastic IP&lt;/strong&gt; comes in. All outbound traffic from your private subnet exits through that single, predictable address (or a small set of addresses for high availability).&lt;/p&gt;

&lt;p&gt;The same logic applies to ingress. If a partner organization controls your DNS but does not control your infrastructure, they will need stable IPs for the records they manage.&lt;br&gt;
Also, if your users are behind a corporate firewall and ask for a list of IPs to whitelist, the same situation can arise.&lt;/p&gt;

&lt;p&gt;Auto-provisioned load balancer IPs that change on recreation are a frequent source of friction. Wherever possible, allocate static public IPs (Elastic IPs in AWS, reserved IPs in GCP) and assign them deliberately.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
 Always allocate static IPs early. Retrofitting them after a third-party integration is already pointing at a dynamic address is painful.&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Choose the Right Compute Type for Each Workload
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html" rel="noopener noreferrer"&gt;Spot instances&lt;/a&gt; and preemptible VMs are tempting because the discount can reach 70 to 90 percent. However, the cloud provider can reclaim them at any time, often with only a couple of minutes of warning.&lt;/p&gt;

&lt;p&gt;A simple rule of thumb:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stateful workloads&lt;/strong&gt; (databases, primary application servers holding session state, anything you cannot kill mid-task) should never run on spot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stateless but critical workloads&lt;/strong&gt; such as your main API serving customer traffic should stick to on-demand or reserved instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch jobs, build runners, ML training, queue workers, and ephemeral dev environments&lt;/strong&gt; are excellent candidates for spot, because the workload can be interrupted and resumed safely.
I have personally seen a team get burned by this. They moved their main production fleet to spot to save money, hit a regional capacity shortage, and lost a chunk of their fleet within minutes. The cost savings did not come close to covering the downtime.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;br&gt;
Use spot for what spot is good at: interruptible, idempotent work. Treat your customer-facing serving capacity as reserved infrastructure.&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Never Hardcode Environment-Specific Values
&lt;/h2&gt;

&lt;p&gt;This one shows up most often with cloud resource identifiers. The classic example is the S3 bucket name (or GCS bucket, or Azure blob container).&lt;/p&gt;

&lt;p&gt;A bad pattern looks like this: storing the &lt;strong&gt;full&lt;/strong&gt; path including the bucket name in your database alongside every object reference. Six months later you need to migrate to a different region, or split workloads into a separate account, and now you have to backfill millions of database rows.&lt;/p&gt;

&lt;p&gt;The better pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Store only the &lt;strong&gt;object key (path)&lt;/strong&gt; in the database.&lt;/li&gt;
&lt;li&gt;Store the &lt;strong&gt;bucket name&lt;/strong&gt; as an environment variable read by the application.
When you migrate, you change one environment variable and redeploy. The database stays untouched.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// bad - bucket name baked into stored data&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;objectUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;s3://my-prod-bucket-2026/uploads/user-123/avatar.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// good - bucket comes from config, only the key is persisted&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UPLOADS_BUCKET&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// e.g. "my-prod-bucket-2026"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;objectKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uploads/user-123/avatar.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// stored in DB&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;objectUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`s3://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;objectKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// construct the full object URL dynamically&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same principle applies to API endpoints, region names, queue URLs, and any other value that is environment-dependent. Configuration belongs in environment variables or a configuration service, not in code or in your database.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Manage Infrastructure as Code
&lt;/h2&gt;

&lt;p&gt;Clicking through a cloud console to provision resources is fine for experimenting. It is not fine for production. Every manual change is a snowflake a configuration that exists only in that environment, undocumented, unrepeatable, and invisible until it causes a problem.&lt;/p&gt;

&lt;p&gt;Use an IaC tool like &lt;a href="https://developer.hashicorp.com/terraform" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; to define every resource declaratively, and a configuration management tool like Ansible for anything that runs on the instance itself.&lt;/p&gt;

&lt;p&gt;In practice, this means your all your cloud resources are checked into version control alongside your application code. Every change goes through a pull request, gets reviewed, and is applied via a pipeline not by someone running commands in a terminal. Your staging environment is defined by the same Terraform modules as production, with different variable values.&lt;/p&gt;

&lt;p&gt;The payoff is consistency and confidence: a new environment is a terraform apply away, rollbacks are a git revert away, and the answer to "what changed?" is always in the commit history rather than in someone's memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Tag Every Resource From Day One
&lt;/h2&gt;

&lt;p&gt;Tags are easy to skip on day one and almost impossible to backfill cleanly on day three hundred. Adopt a tagging convention before you start provisioning real resources. A reasonable starter set:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Environment&lt;/code&gt;: production, staging, dev.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Service&lt;/code&gt; or &lt;code&gt;Application&lt;/code&gt;: which component does this belong to.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Owner&lt;/code&gt; or &lt;code&gt;Team&lt;/code&gt;: who is responsible.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CostCenter&lt;/code&gt;: for billing attribution.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ManagedBy&lt;/code&gt;: terraform, cloudformation, manual.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tagging pays off when you start asking questions like "what is our staging environment costing us?", "who owns this orphaned EBS volume?", or "which resources need patching this quarter?".&lt;br&gt;
Without tags, those questions become much harder to answer, leading to wasted time and effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Use Safer Deployment Strategies
&lt;/h2&gt;

&lt;p&gt;A direct in-place deployment to production, where the new version replaces the old one for everyone at once, is the simplest pattern and also the riskiest. If the new version has a bug, every customer hits it instantly and your only path forward is rolling back.&lt;/p&gt;

&lt;p&gt;Two patterns are worth investing in early.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blue-Green deployment&lt;/strong&gt; runs two identical environments (blue and green). You deploy the new version to the idle one, run smoke tests, and then switch traffic over. If something goes wrong, switch back. This works very well with load balancers and managed container services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Canary deployment&lt;/strong&gt; rolls the new version out to a small percentage of traffic first (1 to 10 percent). Monitor error rates, latency, and business metrics. If everything looks healthy, gradually increase the percentage. If not, roll back having only affected a small portion of users.&lt;/p&gt;

&lt;p&gt;Both strategies dramatically reduce blast radius. Pick whichever fits your tooling, but pick something other than "replace everything at once and hope for the best."&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up Part 1
&lt;/h2&gt;

&lt;p&gt;We have covered the infrastructure groundwork: environments that match, networks that protect you, predictable IPs, the right compute for each workload, externalized configuration, consistent tagging, and safer deployments.&lt;/p&gt;

&lt;p&gt;In Part 2 we move into the operational layer: keyless authentication, secrets management and  monitoring. These are the practices that keep you sleeping through the night once you are live on production.&lt;/p&gt;

&lt;h3&gt;
  
  
  What did I miss? Share it in the comments section.
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Thank you for reading. Share if it helped you, and stay tuned for Part 2.
&lt;/h3&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>security</category>
      <category>networking</category>
    </item>
    <item>
      <title>How to Grant Temporary Read-Only Access to a Kubernetes Cluster</title>
      <dc:creator>CodeHiRise</dc:creator>
      <pubDate>Tue, 14 Apr 2026 10:54:30 +0000</pubDate>
      <link>https://dev.to/codehirise/how-to-grant-temporary-read-only-access-to-a-kubernetes-cluster-2go5</link>
      <guid>https://dev.to/codehirise/how-to-grant-temporary-read-only-access-to-a-kubernetes-cluster-2go5</guid>
      <description>&lt;h2&gt;
  
  
  Hi Everyone,
&lt;/h2&gt;

&lt;p&gt;In production Kubernetes environments, giving full cluster admin access to all actor is a major security risk. Instead, you should follow the &lt;strong&gt;principle of least privilege&lt;/strong&gt; and provide only the exact permissions they need.&lt;/p&gt;



&lt;p&gt;This tutorial shows you exactly how an admin can create a limited-access ServiceAccount, attach a Role with the required permissions, generate a short-lived token, and hand over a ready-to-use &lt;code&gt;kubeconfig&lt;/code&gt; file to developers, who would not need full admin access to the cluster but only limited access. In this tutorial we will demonstrate temporary &lt;strong&gt;read-only&lt;/strong&gt; access to pods, deployments, and pod logs.&lt;/p&gt;

&lt;p&gt;By the end of this guide, developers will be able to run commands like:&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
kubectl get deployments
kubectl logs &amp;lt;pod-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while being unable to delete resources, create new ones, or access other namespaces.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why this approach? ServiceAccounts + short-lived tokens are the recommended way in modern Kubernetes for temporary access. They are easy to revoke, don’t require managing client certificates, and expire automatically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s get started&lt;/p&gt;

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

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

&lt;ol&gt;
&lt;li&gt;Admin access to your Kubernetes cluster (kubeconfig with cluster-admin rights).&lt;/li&gt;
&lt;li&gt;kubectl installed and configured.&lt;/li&gt;
&lt;li&gt;A target &lt;strong&gt;namespace&lt;/strong&gt; where developers will work (we’ll use dev-team in this example  change it to your actual namespace).&lt;/li&gt;
&lt;li&gt;Basic familiarity with Kubernetes objects (Pods, Deployments, YAML).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are new to Kubernetes RBAC, don’t worry  we’ll explain every step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create a Dedicated Namespace (Optional but Recommended)
&lt;/h2&gt;

&lt;p&gt;It’s best practice to isolate developer access to a specific namespace.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This ensures developers can only see resources inside dev-team and cannot accidentally affect production namespaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create a ServiceAccount for the Developer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/security/service-accounts/" rel="noopener noreferrer"&gt;ServiceAccounts&lt;/a&gt; are Kubernetes built-in identities for workloads and users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create serviceaccount dev-reader &lt;span class="nt"&gt;-n&lt;/span&gt; dev-team
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a ServiceAccount instead of a real user certificate makes the setup simpler and easier to manage/revoke.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create a Role with Read-Only Permissions
&lt;/h2&gt;

&lt;p&gt;Create a file named dev-reader-role.yaml:&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;rbac.authorization.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;Role&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;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev-team&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;dev-reader-role&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Read-only access to Pods and Deployments&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&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;"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&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;pods"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&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;get"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;list"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;watch"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&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;apps"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&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;deployments"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&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;get"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;list"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;watch"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="c1"&gt;# Allow reading pod logs (very common developer need)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&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;"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&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;pods/log"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&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;get"&lt;/span&gt;&lt;span class="pi"&gt;]&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; dev-reader-role.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rationale for each verb:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  get, list, watch -&amp;gt; Allows viewing resources without modification.&lt;/li&gt;
&lt;li&gt;  pods/log -&amp;gt; Specifically enables kubectl logs command.&lt;/li&gt;
&lt;li&gt;  No create, update, delete, or patch -&amp;gt; Completely read-only.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Bind the Role to the ServiceAccount
&lt;/h2&gt;

&lt;p&gt;Create a file named dev-reader-rolebinding.yaml:&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;rbac.authorization.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;RoleBinding&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;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev-team&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;dev-reader-rolebinding&lt;/span&gt;
&lt;span class="na"&gt;subjects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&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;ServiceAccount&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;dev-reader&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;dev-team&lt;/span&gt;
&lt;span class="na"&gt;roleRef&lt;/span&gt;&lt;span class="pi"&gt;:&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;Role&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;dev-reader-role&lt;/span&gt;
  &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io&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; dev-reader-rolebinding.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The RoleBinding connects the permissions (Role) to the identity (ServiceAccount). Without it, the ServiceAccount has zero permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Generate a Temporary Token
&lt;/h2&gt;

&lt;p&gt;Kubernetes now supports short-lived tokens out of the box.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create token dev-reader &lt;span class="nt"&gt;--namespace&lt;/span&gt; dev-team &lt;span class="nt"&gt;--duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;24h &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dev-reader.token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;--duration=24h&lt;/code&gt; makes the token automatically expire after 24 hours (you can use 1h, 12h, 7d, etc.).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This is much safer than long-lived tokens or certificates.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copy the token (it will be a long JWT string):&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;cat &lt;/span&gt;dev-reader.token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Create the Custom kubeconfig File for the Developer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How to get the CA and API server URL
&lt;/h3&gt;

&lt;p&gt;Run these commands on your admin machine to get required values for kube config.&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;# Get API server URL&lt;/span&gt;
kubectl config view &lt;span class="nt"&gt;--minify&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="s1"&gt;'{.clusters[0].cluster.server}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get CA certificate (base64)&lt;/span&gt;
kubectl config view &lt;span class="nt"&gt;--minify&lt;/span&gt; &lt;span class="nt"&gt;--raw&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="s1"&gt;'{.clusters[0].cluster.certificate-authority-data}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a file named dev-reader.kubeconfig with the following content and replace the placeholders with correct values from above commands&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;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;Config&lt;/span&gt;
&lt;span class="na"&gt;clusters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;certificate-authority-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;YOUR_CLUSTER_CA_BASE64&amp;gt;&lt;/span&gt;   &lt;span class="c1"&gt;# e.g. LS......................&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://&amp;lt;YOUR_API_SERVER_URL&amp;gt;&lt;/span&gt;                  &lt;span class="c1"&gt;# e.g. https://api.yourcluster.com:6443&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;kubernetes&lt;/span&gt;
&lt;span class="na"&gt;contexts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes&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;dev-team&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev-reader&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;dev-reader-context&lt;/span&gt;
&lt;span class="na"&gt;current-context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev-reader-context&lt;/span&gt;
&lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev-reader&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;PREVIOUSLY_GENERATED_TOKEN&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Hand Over the kubeconfig to the Developer
&lt;/h2&gt;

&lt;p&gt;Send the dev-reader.kubeconfig file to the developer securely. The developer can now use it with any kubectl command:&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;# Test access&lt;/span&gt;
kubectl &lt;span class="nt"&gt;--kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev-reader.kubeconfig get pods
kubectl &lt;span class="nt"&gt;--kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev-reader.kubeconfig get deployments
kubectl &lt;span class="nt"&gt;--kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev-reader.kubeconfig logs &amp;lt;pod-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29qpdlqllufk5cjko9mn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29qpdlqllufk5cjko9mn.png" alt="get pods screenshot" width="800" height="69"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84b8b47b1kmp4jhj35py.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84b8b47b1kmp4jhj35py.png" alt="get deployments screenshot" width="800" height="54"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkh89n585k35df15xwtx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkh89n585k35df15xwtx.png" alt="permission check screenshot" width="800" height="61"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71a9zr9ix5t0sqy41j4u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71a9zr9ix5t0sqy41j4u.png" alt="pod logs screenshot" width="800" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They will &lt;strong&gt;not&lt;/strong&gt; be able to run kubectl delete, kubectl apply, or access other namespaces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0xqz7ei9o0v5qoopjd8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0xqz7ei9o0v5qoopjd8.png" alt="delete pods error screenshot" width="800" height="21"&gt;&lt;/a&gt;&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete rolebinding dev-reader-rolebinding &lt;span class="nt"&gt;-n&lt;/span&gt; dev-team
kubectl delete serviceaccount dev-reader &lt;span class="nt"&gt;-n&lt;/span&gt; dev-team
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Security Best Practices Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  Always use &lt;strong&gt;namespace-scoped&lt;/strong&gt; Roles instead of ClusterRoles when possible.&lt;/li&gt;
&lt;li&gt;  Use &lt;strong&gt;short-lived tokens&lt;/strong&gt; (--duration flag).&lt;/li&gt;
&lt;li&gt;  Never share the cluster-admin kubeconfig.&lt;/li&gt;
&lt;li&gt;  Rotate or delete ServiceAccounts regularly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You have now successfully given a developer &lt;strong&gt;temporary, read-only access&lt;/strong&gt; to pods, deployments, and logs in your Kubernetes cluster  with zero risk of destructive actions.&lt;br&gt;
This pattern scales well, you can create different ServiceAccounts for different teams, different namespaces, or even different permission levels (e.g., one for “view only”, another for “view + logs”).&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading. Share if it helped your team!
&lt;/h3&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>security</category>
      <category>iam</category>
    </item>
    <item>
      <title>Integrate Home Assistant InfluxDB and Grafana for Stunning Dashboards</title>
      <dc:creator>CodeHiRise</dc:creator>
      <pubDate>Sun, 24 Aug 2025 19:07:54 +0000</pubDate>
      <link>https://dev.to/codehirise/integrate-home-assistant-influxdb-and-grafana-for-stunning-dashboards-25pc</link>
      <guid>https://dev.to/codehirise/integrate-home-assistant-influxdb-and-grafana-for-stunning-dashboards-25pc</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Hi everyone,&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In this post we will dive in to integrating &lt;a href="https://www.home-assistant.io/" rel="noopener noreferrer"&gt;Home Assistant&lt;/a&gt; with &lt;a href="https://docs.influxdata.com/influxdb/v2/" rel="noopener noreferrer"&gt;InfluxDB V2&lt;/a&gt;, and &lt;a href="https://grafana.com/grafana/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; to store sensor data and create beautiful, real-time dashboards. &lt;/p&gt;

&lt;p&gt;This step-by-step guide is perfect for home automation enthusiasts and IoT developers looking to monitor and visualize sensor data efficiently.&lt;/p&gt;

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

&lt;p&gt;Before we begin, ensure you have the following set up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A working Home Assistant instance.&lt;/li&gt;
&lt;li&gt;InfluxDB v2 installed and accessible (local or cloud-based).&lt;/li&gt;
&lt;li&gt;Grafana installed and running (local or cloud-hosted).&lt;/li&gt;
&lt;li&gt;Basic knowledge of YAML configuration for Home Assistant.&lt;/li&gt;
&lt;li&gt;Access to your InfluxDB and Grafana admin interfaces.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;To achieve real-time dashboards, we need a time-series database to store sensor data from Home Assistant, which Grafana can then use as a data source. InfluxDB is an open-source, high-performance time-series database, making it ideal for this use case. Home Assistant has a built-in integration for InfluxDB, allowing sensor data to be sent to a specified InfluxDB bucket. Grafana then connects to InfluxDB to create visually appealing dashboards for data visualization.&lt;/p&gt;

&lt;p&gt;Below is a high-level architecture diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fum7pe0sp1e9l5x2hibde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fum7pe0sp1e9l5x2hibde.png" alt="Data flows from Home Assistant sensors to InfluxDB, which Grafana queries to create real-time dashboards." width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 Setup home assistant with InfluxDB integration
&lt;/h2&gt;

&lt;p&gt;The InfluxDB integration in Home Assistant is a legacy integration, meaning it must be configured manually via the configuration file (&lt;code&gt;configuration.yaml&lt;/code&gt;) rather than the UI. &lt;/p&gt;

&lt;p&gt;Below, we’ll walk through the steps to set it up.&lt;/p&gt;

&lt;p&gt;Refer &lt;a href="https://www.home-assistant.io/integrations/influxdb/" rel="noopener noreferrer"&gt;InfluxDB integration&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gather InfluxDB Details
&lt;/h3&gt;

&lt;p&gt;You’ll need the following information from your InfluxDB v2 instance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hostname or IP address of your InfluxDB server.&lt;/li&gt;
&lt;li&gt;Port (default is 8086 for InfluxDB v2).&lt;/li&gt;
&lt;li&gt;API Token for authentication.&lt;/li&gt;
&lt;li&gt;Organization ID.&lt;/li&gt;
&lt;li&gt;Bucket Name for storing sensor data.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Finding Your Organization ID
&lt;/h3&gt;

&lt;p&gt;Log in to your InfluxDB v2 web interface. Click your profile in the top-left corner. Select the About tab to view your Organization ID.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5dpuyvzxeg86gu4gcs5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5dpuyvzxeg86gu4gcs5.png" alt="influxdb get organization id ui screenshot" width="800" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Bucket
&lt;/h3&gt;

&lt;p&gt;In the InfluxDB UI, navigate to the Load Data &amp;gt; Buckets section.&lt;/p&gt;

&lt;p&gt;Click Create Bucket.&lt;/p&gt;

&lt;p&gt;Provide a Bucket Name (e.g., home_assistant_data).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft24shyqehxr4ed991twh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft24shyqehxr4ed991twh.png" alt="influxdb create bucket ui screenshot" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Optionally, set a Retention Period (e.g., 30 days) to manage data storage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fra8hyl3hhbq11fcno037.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fra8hyl3hhbq11fcno037.png" alt="influxdb create bucket ui screenshot" width="800" height="634"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating API Token
&lt;/h3&gt;

&lt;p&gt;Go to the Load Data &amp;gt; Tokens section in the InfluxDB UI.&lt;/p&gt;

&lt;p&gt;Click Generate API Token &amp;gt; Custom API Token.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F024lq8a7uo74dwazin7q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F024lq8a7uo74dwazin7q.png" alt="influxdb create token ui screenshot" width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Provide a descriptive name (e.g., Home_Assistant_Write).&lt;/p&gt;

&lt;p&gt;Assign Write permissions to the bucket you just created. This adheres to the &lt;a href="https://en.wikipedia.org/wiki/Principle_of_least_privilege" rel="noopener noreferrer"&gt;least privilege principle&lt;/a&gt; for enhanced security.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F47s0pllbltj529wf4ag1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F47s0pllbltj529wf4ag1.png" alt="influxdb create token ui screenshot" width="800" height="809"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, update your Home Assistant &lt;code&gt;configuration.yaml&lt;/code&gt; file with the InfluxDB integration settings.&lt;/p&gt;

&lt;p&gt;Below is an example configuration that includes specific entities using entity_globs to filter data. &lt;/p&gt;

&lt;p&gt;For example, we’ll include Sonoff smart switches and Zigbee2MQTT temperature sensors. &lt;/p&gt;

&lt;p&gt;You can customize this to include other entities or entire domains as needed.&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;influxdb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;&amp;lt;INFLUXDB_IP&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;POST default - 8086&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;API_TOKEN&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;organization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;ORGANIZATION_ID&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;BUCKET_NAME&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HA&lt;/span&gt;
  &lt;span class="na"&gt;tags_attributes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;friendly_name&lt;/span&gt;
  &lt;span class="na"&gt;default_measurement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;units&lt;/span&gt;
  &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;entity_globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sensor.sonoff*&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sensor.l1_tah_sensor*&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sensor.l2_tah_sensor*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;&amp;lt;INFLUXDB_IP&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;API_TOKEN&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;ORGANIZATION_ID&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;BUCKET_NAME&amp;gt;&lt;/code&gt; with your InfluxDB details.&lt;/p&gt;

&lt;p&gt;Make sure &lt;code&gt;api_version: 2&lt;/code&gt; since we use InfluxDB V2.&lt;/p&gt;

&lt;p&gt;The entity_globs section filters specific entities. Adjust this based on your sensors.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ssl: false&lt;/code&gt; setting assumes a non-SSL connection. Set to &lt;code&gt;true&lt;/code&gt; if your InfluxDB instance uses SSL.&lt;/p&gt;

&lt;p&gt;After updating the configuration, restart Home Assistant to apply the changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify Data in InfluxDB
&lt;/h3&gt;

&lt;p&gt;Log in to the InfluxDB UI.&lt;/p&gt;

&lt;p&gt;Navigate to the Data Explorer section.&lt;/p&gt;

&lt;p&gt;Select your bucket and filter by the entities you configured (e.g., sensor.sonoff*).&lt;/p&gt;

&lt;p&gt;You should see sensor data populating over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If data isn’t appearing, verify your configuration, ensure the sensors are sending data, and confirm the API token has the correct permissions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgn5bwttyh860u3gwz9e8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgn5bwttyh860u3gwz9e8.png" alt="influxdb bucket data explore ui screenshot" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you change home assistant integration configurations, you must restart home assistant to take them into effect.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 Setup InfluxDB integration with Grafana
&lt;/h2&gt;

&lt;p&gt;With data flowing into InfluxDB, it’s time to connect it to Grafana as a data source.&lt;/p&gt;

&lt;p&gt;Log in to your Grafana instance.&lt;/p&gt;

&lt;p&gt;Navigate to Configuration &amp;gt; Data Sources &amp;gt; Add Data Source.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvy80g2nlz6fde8t1r4u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvy80g2nlz6fde8t1r4u.png" alt="grafana add new data source ui screenshot" width="800" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select InfluxDB from the list. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgru7uzbjk7en7qvdggw5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgru7uzbjk7en7qvdggw5.png" alt="grafana add new data source ui screenshot" width="800" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure the data source with the following details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: A descriptive name (e.g., Home_Assistant_InfluxDB).&lt;/li&gt;
&lt;li&gt;Query Language: Select &lt;em&gt;Flux&lt;/em&gt; (since we use InfluxDB v2).&lt;/li&gt;
&lt;li&gt;URL: Enter your InfluxDB URL (e.g., &lt;a href="http://INFLUXDB_IP:8086" rel="noopener noreferrer"&gt;http://INFLUXDB_IP:8086&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Organization: Your InfluxDB &lt;em&gt;Organization name&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Token: Generate a new API token in InfluxDB with Read permissions for your bucket (follow same steps we did earlier. make sure to only provide read permissions).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click Save &amp;amp; Test. You should see a success message confirming the connection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63qvrczgtlwzc4m9wb13.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63qvrczgtlwzc4m9wb13.png" alt="grafana add new data source ui screenshot" width="738" height="1258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Tip: Use separate API tokens for Home Assistant (write) and Grafana (read) adhering to least privileges to maintain secure access control.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 Create Stunning Grafana Dashboards
&lt;/h2&gt;

&lt;p&gt;Now comes the exciting part, building your real-time dashboards in Grafana!&lt;/p&gt;

&lt;p&gt;In Grafana, go to Dashboards &amp;gt; Create Dashboard &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg0bgb5zciolcj146wkff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg0bgb5zciolcj146wkff.png" alt="grafana add new dashboard ui screenshot" width="800" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add Visualization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbo02mvhup4lozamuaad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbo02mvhup4lozamuaad.png" alt="grafana add new dashboard ui screenshot" width="800" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select your newly created InfluxDB data source.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscqv171xthfrikkzgirq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscqv171xthfrikkzgirq.png" alt="grafana add new dashboard ui screenshot" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Write a Flux query to fetch your sensor data. If you’re unsure how to write Flux queries, use the InfluxDB Data Explorer to build and test queries visually.&lt;/p&gt;

&lt;p&gt;In InfluxDB’s Data Explorer, select your bucket and desired entities.&lt;/p&gt;

&lt;p&gt;Filter and aggregate data as needed (e.g., mean temperature over time).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faj6zjaonjsrx8gbkrfng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faj6zjaonjsrx8gbkrfng.png" alt="influxdb bucket data explore ui screenshot" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;em&gt;script editor&lt;/em&gt; to switch to the Script Editor to view the generated Flux query.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsl59gs73fv58e9txndd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsl59gs73fv58e9txndd.png" alt="influxdb bucket data explore ui screenshot" width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the Flux query from InfluxDB and paste it into Grafana’s query editor.&lt;/p&gt;

&lt;p&gt;Click Refresh to visualize the data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0t8lva1hvu014n5drezw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0t8lva1hvu014n5drezw.png" alt="grafana add new data source ui screenshot" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Customize your dashboard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adjust the visualization type (e.g., line graph, gauge, or heatmap).&lt;/li&gt;
&lt;li&gt;Add multiple panels for different sensors (e.g., temperature, humidity, or power usage).&lt;/li&gt;
&lt;li&gt;Use Grafana’s styling options to enhance the look and feel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro Tip: Experiment with Grafana’s Thresholds and Alerts to highlight critical values or receive notifications when sensors report unusual data.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Congratulations! 🥳 You’ve successfully integrated Home Assistant with InfluxDB and Grafana to create stunning, real-time dashboards. &lt;br&gt;
From here, your imagination is the only limit build dashboards to monitor energy usage, track indoor climate, or visualize smart home trends over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Tips
&lt;/h3&gt;

&lt;p&gt;Optimize Data Retention: Set appropriate retention policies in InfluxDB to manage storage efficiently.&lt;/p&gt;

&lt;p&gt;Secure Your Setup: Use SSL for InfluxDB and Grafana in production environments.&lt;/p&gt;

&lt;p&gt;Explore Grafana Plugins: Enhance your dashboards with plugins like the Worldmap Panel for geolocation data.&lt;/p&gt;

&lt;p&gt;Backup Configurations: Regularly back up your Home Assistant and Grafana configurations to avoid data loss.&lt;/p&gt;

&lt;p&gt;For further details, check the official documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.home-assistant.io/integrations/influxdb/" rel="noopener noreferrer"&gt;Home Assistant InfluxDB Integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.influxdata.com/influxdb/v2/get-started/" rel="noopener noreferrer"&gt;InfluxDB Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://grafana.com/docs/grafana/latest/getting-started/build-first-dashboard/" rel="noopener noreferrer"&gt;Grafana Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Thank you for reading, and happy dashboarding!
&lt;/h3&gt;

</description>
      <category>iot</category>
      <category>grafana</category>
      <category>influxdb</category>
      <category>homeautomation</category>
    </item>
    <item>
      <title>Stop Wasting Money 💰 on Idle Jenkins Instances: A Serverless 🚀 Solution to Slash Your AWS Bill</title>
      <dc:creator>CodeHiRise</dc:creator>
      <pubDate>Sun, 09 Mar 2025 17:53:33 +0000</pubDate>
      <link>https://dev.to/codehirise/stop-wasting-money-on-idle-jenkins-instances-a-serverless-solution-to-slash-your-aws-bill-54m1</link>
      <guid>https://dev.to/codehirise/stop-wasting-money-on-idle-jenkins-instances-a-serverless-solution-to-slash-your-aws-bill-54m1</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Hello DevOps Community,&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In many environments, Jenkins instances sit idle between infrequent builds, accruing unnecessary EC2 costs. If your team uses Jenkins only periodically—for deployments, pull requests, or CI/CD pipelines you’re likely overpaying for compute resources.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll explore a serverless solution to &lt;strong&gt;stop the Jenkins instance during idle periods&lt;/strong&gt; and &lt;strong&gt;start it automatically when a new build is triggered&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 &lt;strong&gt;How It Works&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This solution uses two AWS Lambda functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stop Jenkins&lt;/strong&gt;: Monitors activity and shuts down the EC2 instance when idle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start Jenkins&lt;/strong&gt;: Starts the instance when a GitHub webhook triggers a build and forwards the request to Jenkins.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By running the EC2 instance &lt;strong&gt;only during active builds&lt;/strong&gt;, you eliminate idle compute costs.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Important Consideration&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;This approach disrupts scheduled Jenkins jobs (e.g., nightly builds). Ensure your instance operates &lt;strong&gt;purely on-demand via webhook triggers&lt;/strong&gt; and does not rely on schedules.&lt;/p&gt;

&lt;p&gt;🔷 Note that This guide assumes you use the &lt;a href="https://plugins.jenkins.io/github/" rel="noopener noreferrer"&gt;GitHub plugin&lt;/a&gt; to trigger builds on GitHub push events. Adjust the code if your setup differs.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛑 Stop Jenkins workflow
&lt;/h3&gt;

&lt;p&gt;stop Jenkins function will run periodically using event bridge scheduler to check whether Jenkins server is idle every 5 minutes and if it is idle this lambda function will stop the Jenkins server.&lt;/p&gt;

&lt;p&gt;to check jenkins is idle we use &lt;a href="https://javadoc.jenkins-ci.org/hudson/model/computer.html" rel="noopener noreferrer"&gt;Jenkins computer api&lt;/a&gt;&lt;code&gt;/computer/api/json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0yw63x30vm1qyrjiy0p7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0yw63x30vm1qyrjiy0p7.png" alt="Stop Jenkins workflow" width="800" height="128"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;

&lt;span class="n"&gt;ec2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ec2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;jenkins_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;JENKINS_IP&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;INSTANCE_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;EC2_INSTANCE_ID&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;JENKINS_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;jenkins_ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:8080&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;JENKINS_USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;JENKINS_USER&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   
&lt;span class="n"&gt;JENKINS_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;JENKINS_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_instance_state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Instance state: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stopped&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Instance is stopped&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;is_jenkins_idle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop_instances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InstanceIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;INSTANCE_ID&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Instance is idle initiated stop action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Instance is running and Jenkins is busy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error occurred&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_instance_state&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe_instances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InstanceIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;INSTANCE_ID&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Reservations&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Instances&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;State&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_jenkins_idle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Check running builds via executors
&lt;/span&gt;        &lt;span class="n"&gt;computer_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JENKINS_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/computer/api/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;computer_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Basic &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; 
                           &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JENKINS_USER&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JENKINS_TOKEN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to get computer info: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="n"&gt;computer_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;computer_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;computer&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;computer_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;computer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;computer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;idle&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jenkins is idle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error checking Jenkins status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🚀 Start Jenkins workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;When a GitHub webhook triggers:

&lt;ul&gt;
&lt;li&gt;  The &lt;strong&gt;Start Jenkins&lt;/strong&gt; Lambda checks if the instance is running.&lt;/li&gt;
&lt;li&gt;  If stopped, it starts the instance and waits until it’s healthy.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Once Jenkins is active, the Lambda forwards the webhook payload to &lt;code&gt;/github-webhook/&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxybgheqy3dmwt78g75yb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxybgheqy3dmwt78g75yb.png" alt="Start Jenkins workflow" width="800" height="141"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;ec2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ec2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;jenkins_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;JENKINS_IP&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;INSTANCE_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;EC2_INSTANCE_ID&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;JENKINS_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;jenkins_ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:8080&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_instance_state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Instance state: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stopped&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_instances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InstanceIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;INSTANCE_ID&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="nf"&gt;wait_for_instance_running&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="nf"&gt;wait_for_jenkins_ready&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="nf"&gt;trigger_build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Build triggered&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error occurred&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_instance_state&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe_instances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InstanceIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;INSTANCE_ID&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Reservations&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Instances&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;State&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_instance_running&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nf"&gt;get_instance_state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;running&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_jenkins_ready&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JENKINS_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Attempting to connect to Jenkins at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JENKINS_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jenkins is ready: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jenkins is not ready error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trigger_build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JENKINS_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/github-webhook/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lambda-function&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;X-GitHub-Event&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;push&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jenkins triggered build successfully with response: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to trigger Jenkins build: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error response: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Key Benefits&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; 💸  &lt;strong&gt;Cost Savings:&lt;/strong&gt; Pay only for active build time. For example, if you use the Jenkins instance for &lt;strong&gt;2 hours daily&lt;/strong&gt; for active builds, your monthly uptime totals &lt;strong&gt;60 hours&lt;/strong&gt; (versus &lt;strong&gt;720 hours&lt;/strong&gt; for 24/7 operation), resulting in &lt;strong&gt;over 90% cost savings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;✨  &lt;strong&gt;Automation&lt;/strong&gt;: No manual intervention required.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implement this solution to align your Jenkins costs with actual usage! 🚀💰📉&lt;/p&gt;

&lt;p&gt;✋ If you need to use the Jenkins server for extended periods (e.g., debugging), you can either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Temporarily disable the EventBridge scheduler to prevent automatic shutdowns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable stop-protection on the EC2 instance until debugging is complete.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures uninterrupted access while retaining cost optimization as the default behavior.&lt;/p&gt;

&lt;h4&gt;
  
  
  What do you think will you use this on your environment.
&lt;/h4&gt;

&lt;h4&gt;
  
  
  share your thoughts in the comment section.
&lt;/h4&gt;

&lt;h3&gt;
  
  
  Thank you for reading
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Cheers 🥂
&lt;/h3&gt;

</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>cicd</category>
      <category>github</category>
    </item>
    <item>
      <title>Setup a GitHub Self-Hosted Runner on an Ubuntu VM: A DevOps Guide</title>
      <dc:creator>CodeHiRise</dc:creator>
      <pubDate>Sun, 16 Feb 2025 17:03:39 +0000</pubDate>
      <link>https://dev.to/codehirise/setup-a-github-self-hosted-runner-on-an-ubuntu-vm-a-devops-guide-3aim</link>
      <guid>https://dev.to/codehirise/setup-a-github-self-hosted-runner-on-an-ubuntu-vm-a-devops-guide-3aim</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Hi everyone,&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In this article, we will walk you through the steps for setting up GitHub Self-hosted Runners in Ubuntu virtual machine as a service for persistence runner installation.&lt;/p&gt;

&lt;p&gt;The primary reason organizations to adopt GitHub self-hosted runners is to securely access internal, private networks such as databases, services, or virtual machines without exposing them to the public internet. By keeping workflows within their controlled infrastructure, they eliminate risks like accidental data leaks or compliance violations.&lt;/p&gt;

&lt;p&gt;Additionally, self-hosted runners safeguarding sensitive code, credentials, or artifacts since runners are hosted on organization network.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;A GitHub account.&lt;/li&gt;
&lt;li&gt;An Ubuntu Virtual machine with root access with internet connectivity.&lt;/li&gt;
&lt;li&gt;Basic familiarity with Linux commands and GitHub Actions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lets get started&lt;/p&gt;

&lt;p&gt;First go to your desired GitHub repository and go to Settings &amp;gt; Actions &amp;gt; Runners &amp;gt; New self-hosted runner.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhh97sivl36xgcouixhsf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhh97sivl36xgcouixhsf.png" alt="createnew runner github ui" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you click New self hosted runner button you will get below page with all required steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frosle7065l7df6mgbn10.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frosle7065l7df6mgbn10.png" alt="github ui runner setup instructions" width="800" height="766"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It outlines downloading and setting up action runner but in our case we will take some extra steps to make sure our setup is more secure and configure runner as a systemd service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create a Dedicated System User for GitHub action runner
&lt;/h2&gt;

&lt;p&gt;we will create new system user user in our ubuntu vm.&lt;/p&gt;

&lt;p&gt;Run below commad.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--shell&lt;/span&gt; /usr/sbin/nologin gh-action-runner-user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--system&lt;/code&gt;: Creates a non-interactive service account.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--shell /usr/sbin/nologin&lt;/code&gt;: Blocks SSH/login access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ytqwqxaw7ef79pxtuqq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ytqwqxaw7ef79pxtuqq.png" alt="Image description" width="800" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2: Download the GitHub Runner&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Run below command to create runners installation directory in /opt directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /opt/gh-runner
&lt;span class="nb"&gt;cd&lt;/span&gt; /opt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Use the /opt directory for installation of add-on application software package and avoid using user's home directory
&lt;/h4&gt;

&lt;p&gt;Run below command to change ownership of the directory to our current owner for downloading and extracting files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;codehirise:codehirise gh-runner/
&lt;span class="nb"&gt;cd &lt;/span&gt;gh-runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then run below to extract action runner.&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;-o&lt;/span&gt; actions-runner-linux-x64-2.322.0.tar.gz &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/actions/runner/releases/download/v2.322.0/actions-runner-linux-x64-2.322.0.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;optionally run below command to verify downloaded tar file.&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;echo&lt;/span&gt; &lt;span class="s2"&gt;"b13b784808359f31bc79b08a191f5f83757852957dd8fe3dbfcc38202ccf5768  actions-runner-linux-x64-2.322.0.tar.gz"&lt;/span&gt; | shasum &lt;span class="nt"&gt;-a&lt;/span&gt; 256 &lt;span class="nt"&gt;-c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydtorj6gvu8kx1wy5iv9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydtorj6gvu8kx1wy5iv9.png" alt="Image description" width="800" height="21"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next run below command to extact all actions runner tar.gz file.&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;tar &lt;/span&gt;xzf ./actions-runner-linux-x64-2.322.0.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdjqxfnpcwysvk74so93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdjqxfnpcwysvk74so93.png" alt="Image description" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3: Configure the Runner&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After downloading run below command to configure GitHub action runner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./config.sh &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/codehirise/github-actions-private-runner-demo &lt;span class="nt"&gt;--token&lt;/span&gt; YOUR_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Replace YOUR_TOKEN with the token displayed in GitHub add new self hosted action runner page.
&lt;/h4&gt;

&lt;p&gt;Here you will be prompled for some input like runner name and lables. You can enter them as you see fit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flo8ytj9l03wije91wwuh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flo8ytj9l03wije91wwuh.png" alt="Image description" width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will create some additional files like &lt;code&gt;svc.sh&lt;/code&gt; which we will be using to configure action runner as a systemd service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi2ei6sr0lso4o2c3kfuz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi2ei6sr0lso4o2c3kfuz.png" alt="Image description" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before that make sure to change the ownership of the directory to our Linux service user which is &lt;code&gt;gh-action-runner-user&lt;/code&gt;. &lt;br&gt;
Run below command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;gh-action-runner-user:gh-action-runner-user /opt/gh-runner/ &lt;span class="nt"&gt;-R&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1jhjplh3xm7uovh311u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1jhjplh3xm7uovh311u.png" alt="Image description" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 4: Configure the Runner Systemd Service&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Next run below command to install systemd service for github action runner. This will install action runner as a systemd service and set user as &lt;code&gt;gh-action-runner-user&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; ./svc.sh &lt;span class="nb"&gt;install &lt;/span&gt;gh-action-runner-user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next you can run status command to check status&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; ./svc.sh status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and run start command to start github action runner&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; ./svc.sh start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lkc31o1l872yysrv6sa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lkc31o1l872yysrv6sa.png" alt="Image description" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;to check github action run related logs simply run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; actions.runner.codehirise-github-actions-private-runner-demo.gh-private-runner-demo.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Note that actions.runner.codehirise-github-actions-private-runner-demo.gh-private-runner-demo.service is the service name which is displayed when running  sudo ./svc.sh status command.
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3mac2rhndtm614etttsa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3mac2rhndtm614etttsa.png" alt="Image description" width="800" height="115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will show connected to GitHub which indicate that we have successfully setup our GitHub action runner as a service.&lt;br&gt;
if you now visit runners page you can see our brand new github action self hosted runner is ready and waiting to pickup jobs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14yyerdffopbn75wbd68.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14yyerdffopbn75wbd68.png" alt="Image description" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Action runner systemd service comes enabled by default so it will automatically start after a system reboot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdolshfeo3zj6fbnfocc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdolshfeo3zj6fbnfocc2.png" alt="Image description" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Step 5: Using selfhosted runner in Github action workflow&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To use this runner in workflow , set &lt;code&gt;runs-on&lt;/code&gt; in your github actions workflow file to &lt;code&gt;selfhosted&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example workflow file below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This is a basic workflow to help you get started with Actions&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;CI&lt;/span&gt;

&lt;span class="c1"&gt;# Controls when the workflow will run&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Triggers the workflow on push or pull request events but only for the "main" branch&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&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;main"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&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;main"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# Allows you to run this workflow manually from the Actions tab&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="c1"&gt;# A workflow run is made up of one or more jobs that can run sequentially or in parallel&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# This workflow contains a single job called "build"&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# The type of runner that the job will run on&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;self-hosted&lt;/span&gt;

    &lt;span class="c1"&gt;# Steps represent a sequence of tasks that will be executed as part of the job&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="c1"&gt;# Runs a single command using the runners shell&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;Run a one-line script&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo Hello, world!&lt;/span&gt;

      &lt;span class="c1"&gt;# Runs a set of commands using the runners shell&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;Run a multi-line script&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo Add other actions to build,&lt;/span&gt;
          &lt;span class="s"&gt;echo test, and deploy your project.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in action workflow we can see job ran on the runner we setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8byw00z23x66chhryf2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8byw00z23x66chhryf2p.png" alt="Image description" width="800" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our runner logs we can see job completed successfully &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6c1t3ol7tu61mi2oj1li.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6c1t3ol7tu61mi2oj1li.png" alt="Image description" width="800" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Self-hosted GitHub runners on Ubuntu VMs put you in control—locking down sensitive workflows and integrating seamlessly with private infrastructure. With this setup, your pipelines stay fast, secure, and entirely within your environment. Will this be your team’s next step toward DevOps adventure? 🚀&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading.
&lt;/h3&gt;

</description>
      <category>github</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>linux</category>
    </item>
    <item>
      <title>Access EC2 Instances Privately Using AWS Systems Manager</title>
      <dc:creator>CodeHiRise</dc:creator>
      <pubDate>Sat, 30 Dec 2023 15:24:49 +0000</pubDate>
      <link>https://dev.to/codehirise/access-instances-privately-using-aws-systems-manager-28cm</link>
      <guid>https://dev.to/codehirise/access-instances-privately-using-aws-systems-manager-28cm</guid>
      <description>&lt;h2&gt;
  
  
  Hi Everyone,
&lt;/h2&gt;

&lt;p&gt;In this article, we will learn how to access AWS EC2 instances using AWS Systems Manager. This is quite useful,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;when you don't want to expose your instance to public internet via a public ip.&lt;/li&gt;
&lt;li&gt;when you want to manage a large number of instances without sharing ssh keys.&lt;/li&gt;
&lt;li&gt;run commands in many instances without logging in to them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;but this does come with a few prerequisites.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SSM agent should be up and running to connect to this instance (SSM agent comes with Amazon Linux and Ubuntu amis by default).&lt;/li&gt;
&lt;li&gt;The instance should have access to the public internet (or access to SSM endpoints via AWS private endpoints).&lt;/li&gt;
&lt;li&gt;The instance should have a role with the required permissions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;to continue with this post I will assume that you have access to an AWS account and have a basic idea of AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;keep in mind that if you are not in the AWS free tier this will incure you a cost.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the above in mind let's get started.&lt;/p&gt;

&lt;p&gt;First, we will create an aws instance role which is required to grant permissions to the aws instance so that the ssm agent in the ec2 instance can connect with the aws systems manager.&lt;/p&gt;

&lt;p&gt;navigate into aws iam roles and click create a role as below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iZVgDItf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/1createrole1_074bb023bc.png%3Fupdated_at%3D2023-10-21T18:40:32.484Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iZVgDItf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/1createrole1_074bb023bc.png%3Fupdated_at%3D2023-10-21T18:40:32.484Z" alt="create aws role" width="800" height="113"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;in next page add trusted entity EC2 and select &lt;strong&gt;EC2 Role for AWS Systems Manager&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r5VBbedE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/2createrole2_3ebe7d2bcc.png%3Fupdated_at%3D2023-10-21T18:40:35.587Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r5VBbedE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/2createrole2_3ebe7d2bcc.png%3Fupdated_at%3D2023-10-21T18:40:35.587Z" alt="add trusted entity" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;next in permissions since we chose &lt;strong&gt;EC2 Role for AWS Systems Manager&lt;/strong&gt; it will automatically set &lt;strong&gt;AmazonSSMManagedInstanceCore&lt;/strong&gt; policy which is the required permission for this use case.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AGs8V0pH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/3createrole3_d174241e53.png%3Fupdated_at%3D2023-10-21T18:40:33.682Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AGs8V0pH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/3createrole3_d174241e53.png%3Fupdated_at%3D2023-10-21T18:40:33.682Z" alt="add permissions" width="800" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;after hitting next add a name for your role and a description.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GqzX7oOR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/4createrole4_336d782f90.png%3Fupdated_at%3D2023-10-21T18:40:33.393Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GqzX7oOR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/4createrole4_336d782f90.png%3Fupdated_at%3D2023-10-21T18:40:33.393Z" alt="add name for role" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and review whether all settings are mentioned below and click Create role.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fFMNHj_I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/5createrole5_5baf60dc63.png%3Fupdated_at%3D2023-10-21T18:40:35.696Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFMNHj_I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/5createrole5_5baf60dc63.png%3Fupdated_at%3D2023-10-21T18:40:35.696Z" alt="review and create" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and we are done with iam role for our instance. now let's create an ec2 instance.&lt;/p&gt;

&lt;p&gt;navigate to the ec2 menu and click launch instance&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oapZN4q7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/6createinstance_802cbc6d3b.png%3Fupdated_at%3D2023-10-21T18:40:33.486Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oapZN4q7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/6createinstance_802cbc6d3b.png%3Fupdated_at%3D2023-10-21T18:40:33.486Z" alt="launch instance" width="800" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will add an instance name as shown below but this is optional.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zqst9doo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/7createinstance2_01a5803e4f.png%3Fupdated_at%3D2023-10-21T18:40:38.483Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zqst9doo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/7createinstance2_01a5803e4f.png%3Fupdated_at%3D2023-10-21T18:40:38.483Z" alt="add instance name" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;next select ami for this instance I will choose the default Amazon Linux 2023 image for this purpose.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RkybY4ji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/8createinstance3_da0c00ddf2.png%3Fupdated_at%3D2023-10-21T18:40:41.898Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RkybY4ji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/8createinstance3_da0c00ddf2.png%3Fupdated_at%3D2023-10-21T18:40:41.898Z" alt="select ami" width="800" height="733"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;next, specify the instance type and key pair for login. for this example, I will add a keypair since it will be useful if there is an issue with agent connectivity although we are not using it in this tutorial.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MojFsaWh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/9createinstance4_c5c347f3cf.png%3Fupdated_at%3D2023-10-21T18:40:39.795Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MojFsaWh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/9createinstance4_c5c347f3cf.png%3Fupdated_at%3D2023-10-21T18:40:39.795Z" alt="instance type and key pair" width="798" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;next under the network setting, I will remove all inbound access to this instance to demonstrate we can connect without public access. but keep in mind that outbound access is required for ssm agent connectivity, which is added by default when creating a security group.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7cArm45a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/10createinstance5_9a0598ad98.png%3Fupdated_at%3D2023-12-30T15:18:30.499Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7cArm45a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/10createinstance5_9a0598ad98.png%3Fupdated_at%3D2023-12-30T15:18:30.499Z" alt="network settings" width="800" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will keep storage as default as below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mgXC6O8N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/11createinstance6_bf9856d6c5.png%3Fupdated_at%3D2023-10-21T18:40:41.699Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mgXC6O8N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/11createinstance6_bf9856d6c5.png%3Fupdated_at%3D2023-10-21T18:40:41.699Z" alt="storage" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;in the advanced setting we need to add iam role(IAM instance profile) we created in the previous steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ir8kvDTL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/12createinstance7_417ea112c2.png%3Fupdated_at%3D2023-10-21T18:40:41.398Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir8kvDTL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/12createinstance7_417ea112c2.png%3Fupdated_at%3D2023-10-21T18:40:41.398Z" alt="add iam role" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;next hit the launch instance button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ANnFF4nh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/13createinstance8_2107baaedb.png%3Fupdated_at%3D2023-10-21T18:40:43.682Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ANnFF4nh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/13createinstance8_2107baaedb.png%3Fupdated_at%3D2023-10-21T18:40:43.682Z" alt="create instance" width="398" height="815"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and you will get a similar output as below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fEpIE_yn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/14createinstance9_48eef04f63.png%3Fupdated_at%3D2023-10-21T18:40:43.881Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fEpIE_yn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/14createinstance9_48eef04f63.png%3Fupdated_at%3D2023-10-21T18:40:43.881Z" alt="instance created " width="788" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;click on the instance id to navigate to the instance page. Here we can recognize the newly created instance. click on the instance id to view the instance details page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8_PNI8rH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/15createinstance10_a2880a2c37.png%3Fupdated_at%3D2023-10-21T18:40:43.396Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8_PNI8rH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/15createinstance10_a2880a2c37.png%3Fupdated_at%3D2023-10-21T18:40:43.396Z" alt="instanc page" width="800" height="171"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;click on Connect to navigate to the Connect page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uiLIfZa0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/16createinstance11_0ae7c74806.png%3Fupdated_at%3D2023-12-30T15:13:37.287Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uiLIfZa0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/16createinstance11_0ae7c74806.png%3Fupdated_at%3D2023-12-30T15:13:37.287Z" alt="connect" width="800" height="71"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;in the connect page navigate to the session manager tab and press connect&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pvh1LKPn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/17createinstance12_eb49451409.png%3Fupdated_at%3D2023-10-21T18:40:48.190Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pvh1LKPn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/17createinstance12_eb49451409.png%3Fupdated_at%3D2023-10-21T18:40:48.190Z" alt="ec2 connect ssm" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;if you receive an error as below, check the troubleshooting section below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8YtDWvsr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/23errorssm_095bbf45eb.png%3Fupdated_at%3D2023-10-21T19:21:39.984Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8YtDWvsr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/23errorssm_095bbf45eb.png%3Fupdated_at%3D2023-10-21T19:21:39.984Z" alt="error ssm" width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;if everything goes well new tab will be created with a terminal displayed as below. now you are connected to the instance with a terminal.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;note user is ssm user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mchJQVbg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/18createinstance13_0607d571ff.png%3Fupdated_at%3D2023-10-21T18:40:48.097Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mchJQVbg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/18createinstance13_0607d571ff.png%3Fupdated_at%3D2023-10-21T18:40:48.097Z" alt="ssm terminal" width="800" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Troubleshooting.
&lt;/h3&gt;

&lt;p&gt;check if the instance has outbound internet access in security group rules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kksrVyDX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/20sg2_a2bf39d37f.png%3Fupdated_at%3D2023-10-21T18:40:50.888Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kksrVyDX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/20sg2_a2bf39d37f.png%3Fupdated_at%3D2023-10-21T18:40:50.888Z" alt="check sg" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;make sure the instance has iam instance role attached.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l0TfevbI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/21instancerole_2f21f121f0.png%3Fupdated_at%3D2023-10-21T19:16:04.478Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l0TfevbI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/21instancerole_2f21f121f0.png%3Fupdated_at%3D2023-10-21T19:16:04.478Z" alt="check iam role" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;check iam instance role permissions.&lt;br&gt;
only require permissons is &lt;strong&gt;AmazonSSMManagedInstanceCore&lt;/strong&gt; policy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eDN9mmMy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/22iamrolepermissions_557e3457b2.png%3Fupdated_at%3D2023-10-21T19:18:24.280Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eDN9mmMy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/22iamrolepermissions_557e3457b2.png%3Fupdated_at%3D2023-10-21T19:18:24.280Z" alt="permissions" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;check if the instance ssm agent is reporting to the aws ssm fleet manager.&lt;br&gt;
it should be listed as online.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gw2BwTnE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/21fleetmanager_78811b813f.png%3Fupdated_at%3D2023-10-21T18:40:52.289Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gw2BwTnE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/21fleetmanager_78811b813f.png%3Fupdated_at%3D2023-10-21T18:40:52.289Z" alt="fleet manager" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If everything above is in place and you still cannot get ssm connectivity you will need to further troubleshoot issues in ssm agent.&lt;br&gt;
a good place to start troubleshooting is by checking logs.&lt;/p&gt;

&lt;p&gt;check ssm agent logs under &lt;code&gt;/var/log/amazon/ssm&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--79GP2knz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/ssm_err_log_aa15d39632.png%3Fupdated_at%3D2023-12-30T14:46:52.597Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--79GP2knz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/ssm_err_log_aa15d39632.png%3Fupdated_at%3D2023-12-30T14:46:52.597Z" alt="error logs" width="800" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;here we can see there is an access denied error in &lt;code&gt;errors.log&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IdiKcQQw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/ssm_agentlog_a8f665d89d.png%3Fupdated_at%3D2023-12-30T14:46:52.401Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IdiKcQQw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/ssm_agentlog_a8f665d89d.png%3Fupdated_at%3D2023-12-30T14:46:52.401Z" alt="agent logs" width="800" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;here we can see there is an access denied error in &lt;code&gt;amazon-ssm-agent.log&lt;/code&gt; and it is sleeping for 30 minutes.&lt;br&gt;
this could happen if we attach the role after the instance starts,&lt;br&gt;
so ssm agent checked to authenticate and failed then the next retry will be in 30 minutes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JSNUAkga--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/restartssm_8de376a7ff.png%3Fupdated_at%3D2023-12-30T14:46:52.696Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JSNUAkga--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/restartssm_8de376a7ff.png%3Fupdated_at%3D2023-12-30T14:46:52.696Z" alt="restart ssm agent service" width="800" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;we can restart ssm agent so it will try to reauthenticate and succeed this time.&lt;/p&gt;

&lt;p&gt;This is just one scenario of debugging and your specific scenario might change but going through logs will give an insight into what the problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading. Share your thoughts in comments section.
&lt;/h3&gt;

</description>
      <category>aws</category>
      <category>ec2</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>An Introduction To CAP Theorem In Distributed Systems</title>
      <dc:creator>CodeHiRise</dc:creator>
      <pubDate>Mon, 11 Sep 2023 14:12:47 +0000</pubDate>
      <link>https://dev.to/codehirise/an-introduction-to-cap-theorem-in-distributed-systems-3af1</link>
      <guid>https://dev.to/codehirise/an-introduction-to-cap-theorem-in-distributed-systems-3af1</guid>
      <description>&lt;h2&gt;
  
  
  Hello everyone,
&lt;/h2&gt;

&lt;p&gt;Managing distributed computing systems across multiple nodes is a complex challenge. The CAP theorem highlights the intricate trade-offs involved in designing and managing such systems. This theorem centers around three essential attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistency&lt;/li&gt;
&lt;li&gt;Availability&lt;/li&gt;
&lt;li&gt;Partition Tolerance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Collectively referred to as the CAP triad.&lt;/p&gt;

&lt;p&gt;This blog post aims to provide an overview of the CAP theorem, including its significance, implications, and real-world applications.&lt;/p&gt;



&lt;p&gt;The three properties of the CAP theorem are:&lt;/p&gt;

&lt;h3&gt;
  
  
  Consistency
&lt;/h3&gt;

&lt;p&gt;Consistency, in the context of the CAP theorem, refers to the idea that all nodes within a distributed system should have access to the same data at the same time. In other words, regardless of the node you interact with, you should observe the most recent data update. To maintain the same date in all nodes upon an update that data should be replicated to all nodes before the write is considered successful. Maintaining consistency becomes crucial in scenarios where data integrity and correctness are paramount, such as financial systems or databases for critical applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Availability
&lt;/h3&gt;

&lt;p&gt;Availability ensures that every request made to the system should receive a response. However, the response might not always include the most recent data. Availability ensures that a system remains operational and responsive even when individual nodes experience failures or slowdowns. This attribute is crucial in systems where downtime or unresponsiveness is unacceptable, such as e-commerce platforms or real-time analytics systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Partition Tolerance
&lt;/h3&gt;

&lt;p&gt;Partition tolerance refers to the system's ability to continue functioning despite arbitrary network communication failures between nodes. A partition communication error occurs when there are temporary delays or loss of connections between nodes in the distributed system.&lt;/p&gt;

&lt;h3&gt;
  
  
  CAP theorem proof
&lt;/h3&gt;

&lt;p&gt;According to the CAP theorem, it is impossible for a distributed system to simultaneously achieve all three properties. As a result, system designers must make trade-offs and prioritize certain properties based on the specific requirements of their applications.&lt;/p&gt;

&lt;p&gt;Let's assume that we have a 2-node system with consistent, available, and partition tolerance. which means this system can fulfill all three properties.&lt;/p&gt;

&lt;p&gt;At the start, both servers s1 and s2 have the same variable with value v1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z4BqvLPs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/capproof11_fdae8cc2d0.png%3Fupdated_at%3D2023-09-11T11:19:39.188Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z4BqvLPs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/capproof11_fdae8cc2d0.png%3Fupdated_at%3D2023-09-11T11:19:39.188Z" alt="Figure 1" width="800" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;now client will update v1 to v2 value on this variable connecting to s1 server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6g496wnd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/capproof22_6e581c7e92.png%3Fupdated_at%3D2023-09-11T11:19:39.300Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6g496wnd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/capproof22_6e581c7e92.png%3Fupdated_at%3D2023-09-11T11:19:39.300Z" alt="Figure 2" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;now if the client reads the same variable value from s2 it will return v1 which was the previous value breaking consistency in the system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5t9OSjpS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/capproof33_f94662887e.png%3Fupdated_at%3D2023-09-11T11:19:39.897Z" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5t9OSjpS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strapi.rizkyrajitha.xyz/uploads/capproof33_f94662887e.png%3Fupdated_at%3D2023-09-11T11:19:39.897Z" alt="Figure 3" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This happens because the system cannot communicate between two nodes so s1 cannot replicate changes made to s2 node.&lt;/p&gt;

&lt;p&gt;from this we can conclude that we cannot fulfill a consistent, available, partition tolerant system. In this example, we broke consistency over availability and partition tolerance.&lt;/p&gt;



&lt;h4&gt;
  
  
  CA Systems:
&lt;/h4&gt;

&lt;p&gt;Some systems prioritize Consistency and Availability (CA) over Partition Tolerance. Traditional relational databases often fall into this category, where maintaining data integrity and guaranteeing real-time updates are key objectives. However, these systems might struggle during network failures between partitions, potentially leading to downtime.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/"&gt;postgresql&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  CP Systems:
&lt;/h4&gt;

&lt;p&gt;Other systems emphasize Consistency and Partition Tolerance (CP). Such systems ensure data integrity and are designed to function well even in the presence of network partitions. Distributed databases like MongoDB follow this model, delivering consistency at the expense of potential unavailability during connection failures between partitions.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.mongodb.com/"&gt;mongodb&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  MongoDB has tunable levels of consistency for reads and writes. &lt;a href="https://www.mongodb.com/community/forums/t/q-about-mongodbs-cap/150499"&gt;reference&lt;/a&gt;
&lt;/h4&gt;

&lt;h4&gt;
  
  
  AP Systems:
&lt;/h4&gt;

&lt;p&gt;Finally, some systems prioritize Availability and Partition Tolerance (AP). These systems are designed to remain operational even in the face of network disruptions, responding even if the data might be temporarily inconsistent across nodes. NoSQL databases like Apache Cassandra exemplify this approach.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cassandra.apache.org/_/index.html"&gt;cassandra&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Cassandra makes some guarantees about its scalability, availability, and reliability. &lt;a href="https://cassandra.apache.org/doc/4.1/cassandra/architecture/guarantees.html"&gt;reference&lt;/a&gt;
&lt;/h4&gt;

&lt;h3&gt;
  
  
  PACELC Theorem: Extending the CAP Theorem
&lt;/h3&gt;

&lt;p&gt;PACELC theorem extends the CAP theorem by introducing extra factors in to consideration, including latency and consistency, to offer a more comprehensive perspective on distributed systems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It states that in case of network partitioning (P) in a distributed computer system, one has to choose between availability (A) and consistency (C) (as per the CAP theorem), but else (E), even when the system is running normally in the absence of partitions, one has to choose between latency (L) and consistency (C). wikipedia&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;According to the PACELC theorem, when a network partition occurs, a trade-off must be made between availability and consistency. In the absence of a partition, another trade-off arises between latency and consistency. This extended framework acknowledges the importance of latency in real-world applications and highlights the need to consider trade-offs beyond the traditional CAP properties.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading. Share if you loved it.
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Drop your thoughts in comment section
&lt;/h3&gt;

</description>
      <category>networking</category>
      <category>distributedsystems</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Execute Terminal Commands And Receive Live Output With React+SSE</title>
      <dc:creator>CodeHiRise</dc:creator>
      <pubDate>Sat, 09 Sep 2023 19:01:33 +0000</pubDate>
      <link>https://dev.to/codehirise/execute-terminal-commands-and-receive-live-output-with-reactsse-1le4</link>
      <guid>https://dev.to/codehirise/execute-terminal-commands-and-receive-live-output-with-reactsse-1le4</guid>
      <description>&lt;h2&gt;
  
  
  Hello everyone,
&lt;/h2&gt;

&lt;p&gt;This is the second part of Node.js + SSE if you haven't checked out &lt;a href="https://codehirise.com/post/building-a-real-time-command-execution-app-with-node-js-sse" rel="noopener noreferrer"&gt;part one&lt;/a&gt; where we create SSE server I highly recommend you to check it out first.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud0nyqzlquchji3e0y8h.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud0nyqzlquchji3e0y8h.gif" alt="Final output" width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Client
&lt;/h2&gt;

&lt;p&gt;Here i will go with react + vite setup with tailwind as UI, but you can use any framework/library you want since these basics will stay the same.&lt;/p&gt;

&lt;p&gt;I assume that you have installed &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;nodejs&lt;/a&gt; and have a code editor setup, and you have previously created SSE server running on &lt;code&gt;localhost:3000&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn create vite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;i will also install tailwind ui setup. check out &lt;a href="https://tailwindcss.com/docs/guides/vite" rel="noopener noreferrer"&gt;tailwind official doc for setting up&lt;/a&gt; for setting up with react+vite.&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Add the below code snippet to &lt;code&gt;app.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;commandOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCommandOut&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setisActive&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendCommand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// close current event if user submit another command&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;close current event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setCommandOut&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
      &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Empty command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// create event source&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`http://localhost:3000/run?command=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// add eventlistner for message output from command&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stdout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;parsedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsedData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setCommandOut&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;parsedData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stderr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;parsedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsedData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setCommandOut&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;parsedData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;err&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;parsedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsedData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setCommandOut&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;parsedData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setisActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// event handler for command exit to stop retry&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exited&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;setisActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readyState&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;EventSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLOSED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Connection was closed.&lt;/span&gt;
          &lt;span class="nf"&gt;setisActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An error occurred while attempting to connect.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setisActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-slate-800 h-screen&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;container mx-auto &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h-[20vh]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-3xl text-cyan-50 pt-10 font-bold text-center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;Run&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;SSE&lt;/span&gt; &lt;span class="nx"&gt;Demo&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;
            &lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
              &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
              &lt;span class="nf"&gt;sendCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-row justify-between align-middle self-center content-center pt-10 w-full&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
                &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;last_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-gray-50 border border-gray-300 placeholder-gray-600 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-[90%] p-2.5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enter command to run&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;required&lt;/span&gt;
              &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* show only when listening to messeges */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
                &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`content-center flex flex-wrap &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;
                  &lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* spinner animation on active conenction */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;
                  &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;hidden&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inline w-6 h-6 mx-3 text-blue-600 animate-spin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="nx"&gt;viewBox&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0 0 100 101&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="nx"&gt;xmlns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://www.w3.org/2000/svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;
                    &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                    &lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#a6c0d7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;
                    &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                    &lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;currentColor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/svg&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* show connection close button */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
                  &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-white bg-slate-500 hover:bg-slate-600 p-2 font-medium rounded-lg text-sm px-5 mr-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;event listen stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                    &lt;span class="nf"&gt;setisActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                  &lt;span class="p"&gt;}}&lt;/span&gt;
                &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="nx"&gt;Stop&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="p"&gt;)}&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
                &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-white bg-blue-600 hover:bg-blue-800 p-2 font-medium rounded-lg text-sm px-5 &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nx"&gt;Run&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* list down commands with timestamp */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-white pt-10 whitespace-pre h-[80vh] overflow-y-auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;commandOut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mr-2 text-sky-400&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;T&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; : `&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="p"&gt;))}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;EventSource&lt;/code&gt; is where the server SSE integrates with the Client end.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;EventSource&lt;/code&gt; is a javascript API that enables the client side to receive SSE from a server over a single, long-lived HTTP connection.&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Then we need to add event listers to &lt;code&gt;EventSource&lt;/code&gt;. here we need to listen to the events we send from the server end which are &lt;code&gt;stdout&lt;/code&gt;,&lt;code&gt;stderr&lt;/code&gt;, &lt;code&gt;err&lt;/code&gt;, and &lt;code&gt;exit&lt;/code&gt; events.&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;br&gt;
We maintain one &lt;code&gt;useState&lt;/code&gt; for managing command outputs and one for keeping the command execution state since we can execute commands like &lt;code&gt;tail&lt;/code&gt; to get realtime updates of a file that follow file changes realtime.&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;br&gt;
&lt;code&gt;exit&lt;/code&gt; EventListener will close the connection from the client end so it is not automatically retried.&lt;/p&gt;

&lt;p&gt;after we update the state to &lt;code&gt;commandOut&lt;/code&gt;, we need to render ui with event type, time, and command output using the array map method.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;br&gt;
I have added additional functionality cancel where we can close the current connection to stop listening to events. in this case, the client will close the connection and the command executing on the server will also be killed.&lt;/p&gt;



&lt;p&gt;If everything goes well, you will get below UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codehirise/nodejs-react-sse-code-executor/tree/master/client/sse-run-command" rel="noopener noreferrer"&gt;Git repo&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/codehirise" rel="noopener noreferrer"&gt;
        codehirise
      &lt;/a&gt; / &lt;a href="https://github.com/codehirise/nodejs-react-sse-code-executor" rel="noopener noreferrer"&gt;
        nodejs-react-sse-code-executor
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Execute Terminal Commands And Receive Live Output With Nodejs + SSE + React&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Read full article on&lt;/p&gt;

&lt;p&gt;build backend : &lt;a href="https://codehirise.com/post/building-a-real-time-command-execution-app-with-node-js-sse" rel="nofollow noopener noreferrer"&gt;https://codehirise.com/post/building-a-real-time-command-execution-app-with-node-js-sse&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;build frontend :&lt;a href="https://codehirise.com/post/execute-terminal-commands-and-receive-live-output-with-react-sse" rel="nofollow noopener noreferrer"&gt;https://codehirise.com/post/execute-terminal-commands-and-receive-live-output-with-react-sse&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/3ae5d476d36b2d5d127446bfd220c7c34b5fa95f38649141d4a49230265240c4/68747470733a2f2f636f64656869726973652e636f6d2f5f6e6578742f696d6167653f75726c3d6874747073253341253246253246636f64656869726973652e636f6d2532466170692532466f672533467469746c652533444275696c64696e67253230612532305265616c2d54696d65253230436f6d6d616e64253230457865637574696f6e253230417070253230776974682532304e6f64652e6a7325323025324225323053534526773d3132303026713d3735"&gt;&lt;img src="https://camo.githubusercontent.com/3ae5d476d36b2d5d127446bfd220c7c34b5fa95f38649141d4a49230265240c4/68747470733a2f2f636f64656869726973652e636f6d2f5f6e6578742f696d6167653f75726c3d6874747073253341253246253246636f64656869726973652e636f6d2532466170692532466f672533467469746c652533444275696c64696e67253230612532305265616c2d54696d65253230436f6d6d616e64253230457865637574696f6e253230417070253230776974682532304e6f64652e6a7325323025324225323053534526773d3132303026713d3735" alt="cover"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/codehirise/nodejs-react-sse-code-executor" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1i662g80ne3wibutruhj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1i662g80ne3wibutruhj.png" alt="final ui" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading. Share if you loved it.
&lt;/h3&gt;

</description>
      <category>react</category>
      <category>node</category>
      <category>javascript</category>
      <category>networking</category>
    </item>
    <item>
      <title>Building A Real-Time Command Execution App With Node.Js + SSE</title>
      <dc:creator>CodeHiRise</dc:creator>
      <pubDate>Sat, 09 Sep 2023 18:56:37 +0000</pubDate>
      <link>https://dev.to/codehirise/building-a-real-time-command-execution-app-with-nodejs-sse-5gdn</link>
      <guid>https://dev.to/codehirise/building-a-real-time-command-execution-app-with-nodejs-sse-5gdn</guid>
      <description>&lt;h2&gt;
  
  
  Hello everyone,
&lt;/h2&gt;

&lt;p&gt;Real-time communication protocols are essential for applications that require realtime data updates. Two popular options for achieving real-time communication are Server-Sent Events (SSE) and WebSockets. In this two part series, we'll dive into SSE ie Server sent events and build &lt;strong&gt;SSECommander&lt;/strong&gt;, An application that execute terminal commands sent via HTTP and delivers the command output stream real-time through Server-Sent Events (SSE) using Node.js.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud0nyqzlquchji3e0y8h.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud0nyqzlquchji3e0y8h.gif" alt="Final output" width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With server-sent events, a server can push data to the client without the client needing to poll the server to get data once in a while. SSE has some similarities with WebSockets but also has distinct features that make it better for many use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web sockets
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Bi-directional i.e Full duplex&lt;/li&gt;
&lt;li&gt;support Binary and text data&lt;/li&gt;
&lt;li&gt;Uses WebSocket protocol&lt;/li&gt;
&lt;li&gt;Scaling is more complex than SSE&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Server sent events
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Uni directional i.e Half duplex&lt;/li&gt;
&lt;li&gt;support only text data&lt;/li&gt;
&lt;li&gt;Uses HTTP protocol&lt;/li&gt;
&lt;li&gt;Simple and user friendly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;WebSockets are a perfect fit for applications that require real-time collaboration, such as a collaborative drawing app, where both the client and server need to send data realtime. However, it's worth noting that implementing WebSockets can introduce a notable level of complexity to your application and infrastructure. This complexity can add extra development and maintenance efforts.&lt;/p&gt;

&lt;p&gt;On the other hand, if your use case primarily involves the server pushing data to the client without requiring bidirectional communication, Server-Sent Events (SSE) are an excellent choice. SSE offers a simpler and more streamlined design, allowing you to focus more on your business logic instead of spending time on debugging complex WebSocket-related issues. SSE provides a straightforward approach to delivering continuous updates from the server to the client, resulting in an efficient and straightforward implementation process.&lt;/p&gt;

&lt;p&gt;So without further ado let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server
&lt;/h2&gt;

&lt;p&gt;First, we will start with developing the server.&lt;/p&gt;

&lt;p&gt;I assume that you have installed &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;nodejs&lt;/a&gt; and have a code editor setup.&lt;/p&gt;

&lt;p&gt;Let's create a directory and init npm&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;ssedemoserver
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;let's install express and cors to handle cross-origin.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm I express cors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;create app.js file&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;touch &lt;/span&gt;app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will add required libraries. &lt;code&gt;child_process&lt;/code&gt; is required to execute commands in the host machine aka our server. we can also use &lt;code&gt;exec&lt;/code&gt; over &lt;code&gt;spawn&lt;/code&gt; but here I will use &lt;code&gt;spawn&lt;/code&gt; since it will stream data instead of buffering. refere &lt;a href="https://nodejs.org/api/child_process.html#child_processspawncommand-args-options" rel="noopener noreferrer"&gt;nodejs official doc&lt;/a&gt; for more info on that.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// setup cors&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// required to execute commands on host&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;spawn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now let's create the route for sse events. this is a &lt;code&gt;GET&lt;/code&gt; route and the command is sent via http query parameters.&lt;/p&gt;

&lt;p&gt;SSE requires setting some headers to begin.&lt;/p&gt;
&lt;h3&gt;
  
  
  Headers
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;Content-Type: text/event-stream&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;This header specifies the content type of the response as "text/event-stream". It indicates that the server will be sending an SSE stream, which is a text-based format for sending a continuous stream of events to the client.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;Cache-Control: no-cache&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;This header instructs the client and any intermediary caches not to cache the SSE response. SSE is designed for real-time communication, and caching the response would defeat the purpose of receiving continuous updates.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;Connection: keep-alive&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;This header enables a persistent connection between the server and the client. By setting the "Connection" header to "keep-alive", the server informs the client to keep the connection open for further events. This allows the server to send events to the client without having to establish a new connection for each event, improving efficiency and reducing overhead.&lt;/p&gt;
&lt;h3&gt;
  
  
  Data format
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;Event Type&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;This line starts with &lt;code&gt;event:&lt;/code&gt; and specifies the type of event being sent. It is optional but can be useful for client-side event handling.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;Data&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;This line starts with &lt;code&gt;data:&lt;/code&gt; and contains the actual data being sent in the event. Multiple lines can be used for multi-line data. Each line should start with data: and end with &lt;code&gt;\n&lt;/code&gt;. To end message append &lt;code&gt;\n\n&lt;/code&gt; at the end&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;ID&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;This line starts with &lt;code&gt;id:&lt;/code&gt; and provides a unique identifier for the event.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;Retry&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Retry: This line starts with &lt;code&gt;retry:&lt;/code&gt; and specifies the reconnection time in &lt;strong&gt;milliseconds&lt;/strong&gt; if the connection is closed. It allows the client to automatically reconnect after a specified time defaulting to 3s.&lt;/p&gt;

&lt;p&gt;in the below, i will not set &lt;code&gt;retry&lt;/code&gt; since default retry is suitable for this implementation&lt;/p&gt;

&lt;p&gt;I have used &lt;code&gt;stdout&lt;/code&gt;,&lt;code&gt;stderr&lt;/code&gt;, &lt;code&gt;err&lt;/code&gt;, and &lt;code&gt;exit&lt;/code&gt; event types to emphasize the usage of events but you can always simplify that design for better use cases.&lt;/p&gt;

&lt;p&gt;once a command is &lt;code&gt;exited&lt;/code&gt; we will close the connection since there will be no more data to send from the server end. after the response id close from the server client will retry to connect, to eliminate this behavior we will send an exit event so the client will close the connection.&lt;/p&gt;

&lt;p&gt;in the event of a client connection closing, kill the process as we don't want to keep any dangling processes.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// run route handler&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/run&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`command : &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// set headers required for sse `"Content-Type": "text/event-stream",`is where the magic happens&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/event-stream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keep-alive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// send 200 response to client&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// break down command to command and arguments as requried by spaw methode&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runCommand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`pid : &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;runCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// send stdout encoding to uft8 since sse only support text&lt;/span&gt;
  &lt;span class="nx"&gt;runCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setEncoding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// add event listener to list to data on stdout&lt;/span&gt;
  &lt;span class="nx"&gt;runCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stdout : &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// create it for event&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// write id to response&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`id: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// set event&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`event: stdout\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// write data to response&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`data: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stdout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// add event listener to list to data on stderr&lt;/span&gt;
  &lt;span class="nx"&gt;runCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`stderr : &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// write id to response&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`id: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// set event&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`event: stderr\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// write data to response&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`data: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;strerr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// add event listener to list to errors&lt;/span&gt;
  &lt;span class="nx"&gt;runCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error executing command exited : &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// set event&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`event: err\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// write data to response&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`data: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// when command is exit close connection by sending&lt;/span&gt;
  &lt;span class="nx"&gt;runCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`command exited code : &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// set event&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`event: exit\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`data: exited\n\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// set event&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// kill long running command id client connection is close&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remotePort&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Connection closed`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;runCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kill&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;p&gt;setup http server listen with express&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// http server listen on given port&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`App is listening on http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;p&gt;now lets run the application using&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;for testing, I am using &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
SSE support for postman was introduced on version &lt;code&gt;v10.10&lt;/code&gt; and above.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.postman.com/support-for-server-sent-events/" rel="noopener noreferrer"&gt;postman blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;if everything goes well you will get below output. here we can see event data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important Note: It's crucial to understand that executing commands on a server without proper input sanitization can pose significant security risks.The code provided in this example is meant for educational purposes only.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1tl2gxi55lz1i8sy2ak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1tl2gxi55lz1i8sy2ak.png" alt="Postman output" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just like that, we build a server with SSE support.&lt;/p&gt;
&lt;h3&gt;
  
  
  Fun fact: Did you know that ChatGPT uses &lt;code&gt;SSE&lt;/code&gt; to stream responses?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/codehirise/nodejs-react-sse-code-executor/tree/master/server" rel="noopener noreferrer"&gt;Git repo&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/codehirise" rel="noopener noreferrer"&gt;
        codehirise
      &lt;/a&gt; / &lt;a href="https://github.com/codehirise/nodejs-react-sse-code-executor" rel="noopener noreferrer"&gt;
        nodejs-react-sse-code-executor
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Execute Terminal Commands And Receive Live Output With Nodejs + SSE + React&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Read full article on&lt;/p&gt;

&lt;p&gt;build backend : &lt;a href="https://codehirise.com/post/building-a-real-time-command-execution-app-with-node-js-sse" rel="nofollow noopener noreferrer"&gt;https://codehirise.com/post/building-a-real-time-command-execution-app-with-node-js-sse&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;build frontend :&lt;a href="https://codehirise.com/post/execute-terminal-commands-and-receive-live-output-with-react-sse" rel="nofollow noopener noreferrer"&gt;https://codehirise.com/post/execute-terminal-commands-and-receive-live-output-with-react-sse&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/3ae5d476d36b2d5d127446bfd220c7c34b5fa95f38649141d4a49230265240c4/68747470733a2f2f636f64656869726973652e636f6d2f5f6e6578742f696d6167653f75726c3d6874747073253341253246253246636f64656869726973652e636f6d2532466170692532466f672533467469746c652533444275696c64696e67253230612532305265616c2d54696d65253230436f6d6d616e64253230457865637574696f6e253230417070253230776974682532304e6f64652e6a7325323025324225323053534526773d3132303026713d3735"&gt;&lt;img src="https://camo.githubusercontent.com/3ae5d476d36b2d5d127446bfd220c7c34b5fa95f38649141d4a49230265240c4/68747470733a2f2f636f64656869726973652e636f6d2f5f6e6578742f696d6167653f75726c3d6874747073253341253246253246636f64656869726973652e636f6d2532466170692532466f672533467469746c652533444275696c64696e67253230612532305265616c2d54696d65253230436f6d6d616e64253230457865637574696f6e253230417070253230776974682532304e6f64652e6a7325323025324225323053534526773d3132303026713d3735" alt="cover"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/codehirise/nodejs-react-sse-code-executor" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;In the &lt;a href="https://dev.to/codehirise/execute-terminal-commands-and-receive-live-output-with-reactsse-1le4"&gt;second part&lt;/a&gt; of this tutorial, we will build a client ui using react to create a realtime command execute application.&lt;/p&gt;

&lt;p&gt;Aaaaaaand that's a wrap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading.
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Let us know what you think in the comment section.
&lt;/h3&gt;

</description>
      <category>node</category>
      <category>react</category>
      <category>linux</category>
      <category>networking</category>
    </item>
  </channel>
</rss>
