<?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: Kasun de Silva</title>
    <description>The latest articles on DEV Community by Kasun de Silva (@kasundsilva).</description>
    <link>https://dev.to/kasundsilva</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1009425%2F1af61d95-5377-48ae-952b-e9f10f55aa97.jpeg</url>
      <title>DEV Community: Kasun de Silva</title>
      <link>https://dev.to/kasundsilva</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kasundsilva"/>
    <language>en</language>
    <item>
      <title>Beyond Stateless Lambda: Harnessing MicroVMs for Isolated AI Sandboxes</title>
      <dc:creator>Kasun de Silva</dc:creator>
      <pubDate>Tue, 29 Sep 2026 02:23:03 +0000</pubDate>
      <link>https://dev.to/aws-builders/beyond-stateless-lambda-harnessing-microvms-for-isolated-ai-sandboxes-4g6e</link>
      <guid>https://dev.to/aws-builders/beyond-stateless-lambda-harnessing-microvms-for-isolated-ai-sandboxes-4g6e</guid>
      <description>&lt;p&gt;Here is the updated, complete blog post incorporating the AWS Console location and Terraform code specification as dedicated sections.&lt;/p&gt;




&lt;h1&gt;
  
  
  Unlocking Next-Gen AI Sandboxes: Building Secure Code Execution with AWS Lambda MicroVMs
&lt;/h1&gt;

&lt;p&gt;Generative AI agents are shifting from passive text generators to active task executors. Whether it’s an agent running multi-step data analyses, generating and testing Python scripts, or compiling code directly from natural language prompts, giving Large Language Models (LLMs) access to code execution has become a cornerstone of modern AI workflows.&lt;/p&gt;

&lt;p&gt;However, executing AI-generated code introduces a classic developer dilemma: &lt;strong&gt;How do you safely run untrusted code without sacrificing speed or blowing your infrastructure budget?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Traditional solutions often force a compromise. Standard serverless functions are fast and cost-effective, but their strictly stateless nature makes multi-step reasoning loops clunky. On the other hand, traditional container or VM architectures support stateful, long-running processes, but they suffer from slower spin-up times and high idle compute costs.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;AWS Lambda MicroVMs&lt;/strong&gt; a game-changer for AI agent tool execution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Standard Serverless Falls Short for AI Sandboxes
&lt;/h2&gt;

&lt;p&gt;When an AI agent executes code, it rarely does so in a single isolated step. A typical reasoning loop looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Step 1:&lt;/strong&gt; The agent writes a script to scrape data from a source and installs a few dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 2:&lt;/strong&gt; The agent inspects the output, encounters a parsing error, and modifies local files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 3:&lt;/strong&gt; The agent runs a data cleaning function on the saved output and generates a plot.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a &lt;strong&gt;stateless&lt;/strong&gt; function environment, retaining memory, installed packages, and local files across these three distinct turns requires complex external storage workarounds. Every invocation risks feeling like starting from scratch.&lt;/p&gt;




&lt;h2&gt;
  
  
  The AWS Lambda MicroVM Solution
&lt;/h2&gt;

&lt;p&gt;Built on top of Firecracker the hardware-virtualized hypervisor technology designed by AWS Lambda MicroVMs bridge the gap between lightweight serverless compute and stateful virtual machines.&lt;/p&gt;

&lt;p&gt;They provide dedicated, stateful sandboxes with several key advantages tailored specifically for dynamic AI workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hardware-Level Security:&lt;/strong&gt; Each MicroVM runs inside its own lightweight Linux kernel boundary, creating a complete isolation barrier. Even if an LLM generates destructive or buggy code, the blast radius is strictly confined to that individual sandbox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent Session Memory:&lt;/strong&gt; MicroVMs retain full RAM state, local disk directories (&lt;code&gt;/workspace&lt;/code&gt;), installed dependencies, and running background processes for up to 8 hours per session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-Second Suspend &amp;amp; Resume:&lt;/strong&gt; When an AI agent pauses between execution steps, the MicroVM can automatically pause and store its execution snapshot. When the agent dispatches its next tool call, the sandbox resumes instantly without requiring a full cold boot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Vertical Scaling:&lt;/strong&gt; Need to process a sudden surge of data? MicroVMs can vertically scale up to 4x their baseline CPU and RAM on the fly during spike demands.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Architectural Deep Dive: An AI Agent Sandbox Workflow
&lt;/h2&gt;

&lt;p&gt;Here is how a production-ready AI code execution pipeline comes together using AWS Lambda MicroVMs and an LLM orchestration layer (like Amazon Bedrock or Anthropic Claude):&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpt90mo6xqlgz7hyae9o9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpt90mo6xqlgz7hyae9o9.png" alt=" " width="800" height="865"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The End-to-End Workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Trigger:&lt;/strong&gt; The AI agent encounters a tool call requiring code execution and fires an HTTPS request to &lt;strong&gt;Amazon API Gateway&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control &amp;amp; Routing:&lt;/strong&gt; A lightweight &lt;strong&gt;Launcher Lambda&lt;/strong&gt; verifies authentication signatures, checks session metadata in &lt;strong&gt;Amazon DynamoDB&lt;/strong&gt;, and requests a MicroVM environment via programmatic control APIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Boot:&lt;/strong&gt; AWS spins up a MicroVM pre-configured with standard runtime binaries (Python, Node, git) using snapshot restoration in sub-second time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolated Execution:&lt;/strong&gt; The generated script runs inside the guest kernel's &lt;code&gt;/workspace&lt;/code&gt; environment. Stdout, stderr, and output artifacts are captured and streamed back to the orchestrator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idle Suspend:&lt;/strong&gt; Once execution finishes, the MicroVM enters an idle phase and automatically suspends its state, ensuring you only pay for active execution time and stored snapshots rather than continuous idle compute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuation or Tear-Down:&lt;/strong&gt; Subsequent code executions during the same session resume seamlessly from the suspended snapshot. When the session reaches its lifecycle cap or completes, the environment is cleanly destroyed.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Navigating MicroVMs in the AWS Console
&lt;/h2&gt;

&lt;p&gt;Unlike standard Lambda Functions which operate as stateless event-handlers, AWS treats MicroVMs as a distinct, lifecycle-managed compute resource.&lt;/p&gt;

&lt;p&gt;To locate and manage them in the &lt;strong&gt;AWS Management Console&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Head over to the &lt;strong&gt;AWS Lambda&lt;/strong&gt; main dashboard.&lt;/li&gt;
&lt;li&gt;Look at the left-hand navigation menu under the &lt;strong&gt;Compute&lt;/strong&gt; section you’ll find a dedicated &lt;strong&gt;MicroVMs&lt;/strong&gt; tab alongside &lt;em&gt;Functions&lt;/em&gt; and &lt;em&gt;Layers&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;From this interface, you can manage two core primitives:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MicroVM Images:&lt;/strong&gt; Build, view, and version snapshot images (restored from S3 base code or Dockerfile builds).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active Sessions:&lt;/strong&gt; Monitor running, suspended, or terminated MicroVM instances, inspect auto-suspend triggers, and obtain generated session HTTPS/gRPC endpoints.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpjhgsl5u1dv2i5vmyze6.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpjhgsl5u1dv2i5vmyze6.png" alt=" " width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Infrastructure as Code: Specifying MicroVMs in Terraform
&lt;/h2&gt;

&lt;p&gt;AWS Lambda MicroVMs feature a distinct API surface from standard functions. In Terraform (using the &lt;code&gt;hashicorp/aws&lt;/code&gt; provider), you manage them via &lt;code&gt;aws_lambdamicrovms_microvm&lt;/code&gt; and &lt;code&gt;aws_lambda_microvm_image&lt;/code&gt; resources rather than &lt;code&gt;aws_lambda_function&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is a snippet illustrating how to define an image snapshot and spin up an auto-suspending session sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 1. Define the MicroVM Base Image Snapshot&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_microvm_image"&lt;/span&gt; &lt;span class="s2"&gt;"ai_sandbox_image"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python-ai-sandbox-image"&lt;/span&gt;
  &lt;span class="nx"&gt;base_image_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:lambda:us-east-1:aws:microvm-image:al2023-1"&lt;/span&gt;
  &lt;span class="nx"&gt;build_role_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microvm_build_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;

  &lt;span class="nx"&gt;code_artifact&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;s3_bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-ai-agent-artifacts-bucket"&lt;/span&gt;
    &lt;span class="nx"&gt;s3_key&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sandboxes/python-runtime.zip"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cpu_architecture&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arm64"&lt;/span&gt;
    &lt;span class="nx"&gt;baseline_vcpus&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;baseline_memory&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Provision the Stateful MicroVM Sandbox Instance&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambdamicrovms_microvm"&lt;/span&gt; &lt;span class="s2"&gt;"agent_session"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image_arn&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_microvm_image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ai_sandbox_image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;execution_role_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;microvm_execution_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;

  &lt;span class="c1"&gt;# Idle Policy: Auto-suspend when idle for 5 mins&lt;/span&gt;
  &lt;span class="nx"&gt;idle_policy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;auto_resume_enabled&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;max_idle_duration_seconds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Max session lifespan (up to 8 hours)&lt;/span&gt;
  &lt;span class="nx"&gt;maximum_duration_in_seconds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;28800&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# 3. Output the Dedicated Sandbox HTTPS Endpoint&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"microvm_session_endpoint"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Dedicated HTTPS endpoint for AI Agent tool calls"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambdamicrovms_microvm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;agent_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Verdict
&lt;/h2&gt;

&lt;p&gt;For teams building AI agents, interactive browser IDEs, or multi-tenant SaaS tools that demand secure code execution, &lt;strong&gt;AWS Lambda MicroVMs&lt;/strong&gt; remove the trade-off between strict security and low latency.&lt;/p&gt;

&lt;p&gt;By pairing hardware-isolated Firecracker virtual machines with auto-suspend lifecycle controls, you get the security of full virtualization alongside the elasticity and cost benefits of serverless compute.&lt;/p&gt;




&lt;p&gt;You can learn more about how AWS Lambda MicroVMs deliver isolated code execution at scale in &lt;a href="https://www.youtube.com/watch?v=lIOjTOGh-po" rel="noopener noreferrer"&gt;Introducing AWS Lambda MicroVMs&lt;/a&gt;. This official overview video provides additional detail on Firecracker hardware isolation and snapshot-based fast starts for stateful AI workloads.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>lambda</category>
      <category>ai</category>
    </item>
    <item>
      <title>Architecting an Enterprise Network on AWS Cloud WAN</title>
      <dc:creator>Kasun de Silva</dc:creator>
      <pubDate>Thu, 24 Sep 2026 04:31:22 +0000</pubDate>
      <link>https://dev.to/aws-builders/architecting-an-enterprise-network-on-aws-cloud-wan-14fi</link>
      <guid>https://dev.to/aws-builders/architecting-an-enterprise-network-on-aws-cloud-wan-14fi</guid>
      <description>&lt;p&gt;&lt;em&gt;How to design a multi-region, multi-account AWS network on Cloud WAN, from segments and attachments to single-hop inspection, and the lessons from running one in production.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most enterprise AWS networks grow the same way. One VPC becomes fifty. One region becomes three or four. A Transit Gateway appears in each region, they get peered, and a team spends a growing share of its week keeping route tables in agreement across all of them. It works, and it gets a little more brittle every quarter.&lt;/p&gt;

&lt;p&gt;AWS Cloud WAN is the answer to that pattern. I have run it in production for a network of a few hundred accounts and several hundred VPCs across four regions, and this post is the guide I wish I had at the start: how regions connect, how segments carve up the network, how inspection works, how to attach VPCs at scale, and what to expect operationally. It is a design guide, not a tutorial. There is no console clicking here.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem Cloud WAN solves
&lt;/h2&gt;

&lt;p&gt;A Transit Gateway is regional. Run in four regions and you run four of them, peer them in a mesh, and keep the route tables on all four telling a consistent story. Every new VPC is an attachment, a route table association, propagation into the right tables, and often a static route or two. Every new environment or partner multiplies that.&lt;/p&gt;

&lt;p&gt;Two problems dominate at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Routing intent is spread everywhere.&lt;/strong&gt; No single document says "test can reach shared services, and nothing else." That intent lives in route tables in four regions, and you prove it by reading all of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inspection is double-hop.&lt;/strong&gt; Traffic between VPCs in different regions goes through a firewall leaving the source region and another entering the destination region. Stateful firewalls need to see both directions of a flow, and with independent regional Transit Gateways the only way to guarantee symmetry is to inspect at both ends. Twice the traversals, twice the logs, two rule sets that must agree.&lt;/p&gt;

&lt;p&gt;Cloud WAN removes both problems, and that is the reason to adopt it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud WAN in one paragraph
&lt;/h2&gt;

&lt;p&gt;Cloud WAN gives you a &lt;strong&gt;global network&lt;/strong&gt;, which holds a &lt;strong&gt;core network&lt;/strong&gt; that AWS operates for you. The core network has an &lt;strong&gt;edge&lt;/strong&gt; in each region you choose, and AWS connects those edges over its backbone. You attach VPCs, Transit Gateways, Direct Connect gateways and VPNs to the edges. Every attachment lands in a &lt;strong&gt;segment&lt;/strong&gt;, a routing domain that spans all edges. The whole thing, every segment, every rule about which segment can reach which, and where traffic gets inspected, is described in one versioned JSON document: the &lt;strong&gt;core network policy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If Transit Gateway is a router you configure, Cloud WAN is a WAN you describe.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frsnam4nlzzed8poo47su.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frsnam4nlzzed8poo47su.png" alt=" " width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading the diagram
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Columns are regional edges.&lt;/strong&gt; Everything inside a column is attached to that edge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rows are segments.&lt;/strong&gt; A segment exists on every edge, so cards in the same row reach each other across regions for free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Green links&lt;/strong&gt; are the shared services hub reaching into each environment. Two-way, never transitive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dashed orange arrows&lt;/strong&gt; are internet egress. Every VPC drains to its region's inspection VPC, which holds the NAT and internet gateway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solid orange paths&lt;/strong&gt; are two production flows to another cloud, inspected once each and exiting through the nearest Direct Connect landing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The VPNs have no arrows&lt;/strong&gt; because their path is the same as the orange one, with the VPN in place of the gateway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rest of the post explains each of these in turn.&lt;/p&gt;

&lt;h2&gt;
  
  
  Regions: edges, not peerings
&lt;/h2&gt;

&lt;p&gt;Each region you list in the policy becomes a core network edge. AWS peers every edge to every other edge. You never create an inter-region peering attachment, and you never maintain inter-region routes. When a VPC attaches in Sydney, its CIDR is learned by the edges in Virginia and Oregon automatically, inside whichever segment it joined. Inter-region routing is just intra-segment routing.&lt;/p&gt;

&lt;p&gt;The default behaviour is region affinity. Traffic stays in its home region wherever it can and crosses the backbone only when the destination or a policy says so. That gives predictable paths by default, and you override it deliberately when you want a particular region to handle something, such as inspection or egress.&lt;/p&gt;

&lt;p&gt;An edge is a router you never see. Every attachment in a region terminates on it, it holds one route table per segment, and AWS peers it to every other edge. It has no internet gateway and no firewall of its own. Anything that needs either has to be a VPC you attach to it, which is why the inspection VPCs carry both jobs below.&lt;/p&gt;

&lt;p&gt;Each edge needs its own ASN from a range you reserve in the policy. Pick a private range, keep it out of anything you peer with, and never change it afterwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Segments: the routing intent, written down
&lt;/h2&gt;

&lt;p&gt;Segments are where the design lives. A segment is a routing domain that exists on every edge. Attachments in the same segment can reach each other across regions. Attachments in different segments cannot, unless the policy shares routes between them.&lt;/p&gt;

&lt;p&gt;An enterprise segment model that works well has four groups.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Isolation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Environment&lt;/td&gt;
&lt;td&gt;One segment each for test, staging and production workloads&lt;/td&gt;
&lt;td&gt;Isolated. A workload segment sees itself and shared services, nothing else.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared services&lt;/td&gt;
&lt;td&gt;Central logging, DNS, egress, platform tooling&lt;/td&gt;
&lt;td&gt;Shared into every environment segment across all regions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External&lt;/td&gt;
&lt;td&gt;VPNs, SaaS platforms and partners that do not fit the internal pattern&lt;/td&gt;
&lt;td&gt;One per environment. Isolated, so external parties never see each other.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inter-cloud&lt;/td&gt;
&lt;td&gt;Footprints in other public clouds&lt;/td&gt;
&lt;td&gt;One per environment. Isolated, routed through inspection.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three rules make this model hold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared services is a hub, not a bridge.&lt;/strong&gt; Every environment segment can reach shared services, but sharing is not transitive. Test cannot reach production by going through shared services. The policy shares one segment's routes into another explicitly, and you only share in one direction: into the hub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Split external and inter-cloud by environment.&lt;/strong&gt; One external segment is simpler. Three means a test VPN endpoint can never be a path into production, and inspection rules for each environment stay separate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use segment isolation for anything third-party.&lt;/strong&gt; Isolation is a segment attribute. Set it and attachments inside the segment cannot reach each other, only what you share in. Every partner VPN becomes an island that reaches exactly what you routed to it.&lt;/p&gt;

&lt;p&gt;Written out, the reachability of the whole network fits in one table. This is what the policy encodes, and it applies identically on every edge.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Segment&lt;/th&gt;
&lt;th&gt;Can reach&lt;/th&gt;
&lt;th&gt;Isolated&lt;/th&gt;
&lt;th&gt;Inspected&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Test&lt;/td&gt;
&lt;td&gt;Test and shared services, all regions&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Egress only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Staging&lt;/td&gt;
&lt;td&gt;Staging and shared services, all regions&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Egress only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production&lt;/td&gt;
&lt;td&gt;Production and shared services, all regions&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Egress only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared services&lt;/td&gt;
&lt;td&gt;Every environment segment, all regions&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Egress only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External (per environment)&lt;/td&gt;
&lt;td&gt;Its own environment, via inspection&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Every flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inter-cloud (per environment)&lt;/td&gt;
&lt;td&gt;Its own environment, via inspection&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Every flow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The segment count stays small. Twelve to fifteen segments covers most enterprises. If you find yourself creating a segment per team, you are rebuilding route tables under a new name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attachments: make joining the network boring
&lt;/h2&gt;

&lt;p&gt;At a few hundred accounts, attaching a VPC has to be self-service or your network team becomes a ticket queue. The pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Share the core network to every member account with AWS Resource Access Manager. One share, one organisational unit.&lt;/li&gt;
&lt;li&gt;Let the VPC owner create the attachment in their own account, through whatever internal platform or Terraform module they already use.&lt;/li&gt;
&lt;li&gt;Have the attachment carry a tag naming its segment. The tag is the only thing the owner controls.&lt;/li&gt;
&lt;li&gt;Write an attachment policy in the core network policy that matches the tag and places the attachment in the segment. No human chooses a segment for a VPC.&lt;/li&gt;
&lt;li&gt;Let route propagation do the rest. The VPC's CIDR appears in the segment's route table on every edge.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Turn attachment acceptance off for internal segments. If your network team manages both the core network and the accounts that attach to it, a manual approve step adds friction and no security, because segment placement is already decided by the policy, not by whoever created the attachment. Keep acceptance on for external segments where a third party could be on the other end.&lt;/p&gt;

&lt;p&gt;Enable appliance mode on attachments to inspection VPCs, and only those. Appliance mode pins both directions of a flow to the same availability zone, which stateful firewalls need. Workload VPCs do not need it and it costs you a cross-AZ hop if you turn it on everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspection: one hop, not two
&lt;/h2&gt;

&lt;p&gt;An inspection VPC is a VPC with a stateful firewall in it, attached to the core network, that traffic is steered through. Cloud WAN steers that traffic with &lt;strong&gt;network function groups&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You put inspection VPCs into a group, then in the policy you say which segment-to-segment flows must go &lt;em&gt;via&lt;/em&gt; that group. That is a send-via rule, and it is how you inspect east-west traffic between environments, or between an environment and a partner. There is also send-to, which sends a segment's traffic &lt;em&gt;to&lt;/em&gt; a group, typically a default route, and is how you do centralised egress.&lt;/p&gt;

&lt;p&gt;Egress deserves a sentence of its own because it surprises people. Every segment, shared services included, gets a send-to rule that installs a default route towards the group, and the inspection VPC holds the NAT and internet gateway. Nothing else on the network has a way out. A workload VPC with its own NAT gateway is a hole in the design, so do not build one.&lt;/p&gt;

&lt;p&gt;The win is single-hop inspection. Because the core network sees the whole flow end to end, it can send both directions through the same firewall and keep the flow symmetric even when source and destination are in different regions. An inter-region flow crosses one firewall, not two. Fewer traversals, less log volume, one rule set to reason about, and far simpler troubleshooting.&lt;/p&gt;

&lt;p&gt;Which region's firewall handles a cross-region flow? The policy decides. You can prefer the region closest to the source, set an explicit priority order, or override per edge. That lets you route a small region's traffic through a neighbour's firewall until the small region justifies its own, and fail over when a firewall needs maintenance.&lt;/p&gt;

&lt;p&gt;Design the groups per environment. One group for production inspection, one for lower environments, one for egress. Sharing a firewall between production and test saves money and blurs a boundary you spent the segment model building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hybrid and multi-cloud: same edges, same segments
&lt;/h2&gt;

&lt;p&gt;Everything that is not a VPC attaches to the same core network and lands in a segment like anything else.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Other clouds&lt;/strong&gt; connect over a Direct Connect gateway attachment. Many enterprises deliver those circuits through a connectivity provider rather than dedicated fibre at every site. Land them in the inter-cloud segments and send all their traffic via inspection. Land the same cloud in two regions. Both gateways advertise the same prefixes into the segment, each edge prefers the nearer one, and if a circuit drops its routes withdraw and every edge converges on the other. The two gateways never talk to each other, and a VPC in Oregon does not cross the Pacific to reach a cloud it can exit to locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPNs and partners&lt;/strong&gt; land in external segments, one per environment, each isolated so partners never share a routing domain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Existing Transit Gateways&lt;/strong&gt; can attach as route table attachments. That is a bridge, not a native attachment type, and it is how you keep older things running while you migrate them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is one place to look. A partner VPN, a link to another cloud and a production VPC are rows in the same attachment list, placed by the same policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating from Transit Gateway without a big bang
&lt;/h2&gt;

&lt;p&gt;Cloud WAN and Transit Gateway coexist well, and that is the migration.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build the core network beside the Transit Gateways.&lt;/strong&gt; Peer each regional Transit Gateway to its local edge. The Transit Gateways' prefixes propagate into Cloud WAN, so Cloud WAN already knows how to reach everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attach VPCs without moving traffic.&lt;/strong&gt; Each VPC gets a Cloud WAN attachment and its CIDR propagates, but its route tables still point at the Transit Gateway. Verify the routes are learned on every edge before going further.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cut over with managed prefix lists.&lt;/strong&gt; Add Cloud WAN routes to VPC route tables using a prefix list, so a growing set of destinations moves to Cloud WAN while the Transit Gateway stays the default. You move routes per environment, per region, and roll back by editing a prefix list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flip the default.&lt;/strong&gt; Once a VPC's traffic is all on Cloud WAN, point its default route at the core network and remove the Transit Gateway attachment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Go test, then staging, then production. Run Route Analyzer before and after every step to confirm paths, and run synthetic connectivity tests across attachments to catch what Route Analyzer cannot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operating it: what to know before you commit
&lt;/h2&gt;

&lt;p&gt;The design above is the good news. These are the things that will shape your day-to-day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policy changes take minutes.&lt;/strong&gt; Applying a new policy version, and rolling one back, takes minutes rather than seconds. On a core network carrying production that is a long time to watch a change land. Test in a lower environment first, and know exactly which version you are rolling back to before you press the button.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Everything is one document.&lt;/strong&gt; That is the strength and the hazard. A mistake in one attachment rule affects every region at once. Treat the policy like production code: Terraform, pull requests, plan output reviewed, applied by a pipeline, never edited in the console.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The console diff gets noisy.&lt;/strong&gt; A large policy does not always make a small change look small in the change view. Review the diff in source control first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permissions cut across accounts and services.&lt;/strong&gt; The roles for the team that runs the core network, and for the automation that creates attachments in member accounts, take real least-privilege work. Start that with your security team early, not the week before production cutover.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability is on you.&lt;/strong&gt; Cloud WAN gives you route tables, attachment state and a Route Analyzer. It does not give you flows. Ship VPC flow logs from every attachment and every inspection VPC into one queryable store. That is where you will actually see what the network is doing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost is per edge and per attachment.&lt;/strong&gt; You pay an hourly rate for each core network edge and each attachment, plus data processing. There is no per-request billing. A dormant region with an edge still costs money, so only list regions you actually use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it worth it?
&lt;/h2&gt;

&lt;p&gt;For a multi-region, multi-account network, yes. You replace a mesh of regional Transit Gateways and their route tables with one policy. You halve firewall traversals for inter-region traffic. Attaching a VPC becomes a tag. And "what can test reach?" is answered by a file instead of a scavenger hunt through four regions.&lt;/p&gt;

&lt;p&gt;Cloud WAN is still young and it shows in change velocity and tooling. But the model is right. Describe the WAN, and let AWS run it.&lt;/p&gt;

&lt;p&gt;If you are running a Transit Gateway mesh and weighing this up, I am happy to compare notes. Find me on &lt;a href="https://blog.kasun.dev" rel="noopener noreferrer"&gt;blog.kasun.dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>network</category>
    </item>
    <item>
      <title>Orchestrating Sub-Agents for Cost-Efficient Engineering</title>
      <dc:creator>Kasun de Silva</dc:creator>
      <pubDate>Wed, 23 Sep 2026 23:27:05 +0000</pubDate>
      <link>https://dev.to/kasundsilva/orchestrating-sub-agents-for-cost-efficient-engineering-1pl3</link>
      <guid>https://dev.to/kasundsilva/orchestrating-sub-agents-for-cost-efficient-engineering-1pl3</guid>
      <description>&lt;p&gt;&lt;em&gt;How I route Claude Code work to the cheapest model that can do it well, and the exact rules I use.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Kasun de Silva · September 2026&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;In Claude Code, every subagent you spawn inherits the parent's model unless you say otherwise. If your main session runs on the most capable model, so does the agent you sent to grep sixty repos for a variable name. That is a cron job running on your biggest instance type because it was the one already booted.&lt;/p&gt;

&lt;p&gt;I fixed this with one section in my global CLAUDE.md. The idea: one expensive brain, many cheap hands.&lt;/p&gt;

&lt;p&gt;The brain is the main session model, Fable 5.1 at the time of writing. It plans, briefs, reviews and commits. It does not grep. The hands are subagents on cheaper models. Each gets one bounded job and one precise brief, does it, reports back, and never touches git. Control plane and data plane, if you like.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rules
&lt;/h2&gt;

&lt;p&gt;Copy this into &lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt; and replace "Fable" with whatever your main model is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Global rules&lt;/span&gt;

&lt;span class="gu"&gt;## Orchestration and model routing&lt;/span&gt;

Fable is the planner, sole decision maker, orchestrator, reviewer and committer. Cheaper models do the bulk of the work under Fable's direction. The quality bar is the highest in every case; delegation never lowers it.

&lt;span class="gu"&gt;### Always set the model explicitly&lt;/span&gt;
Subagents inherit the parent model unless &lt;span class="sb"&gt;`model`&lt;/span&gt; is passed. A delegated task without &lt;span class="sb"&gt;`model:`&lt;/span&gt; runs on Fable and saves nothing. Every &lt;span class="sb"&gt;`Agent`&lt;/span&gt; call must name a model.

| Model | Use for |
|---|---|
| &lt;span class="sb"&gt;`haiku`&lt;/span&gt; | Read-only sweeps: grep/find across repos, locating files or symbols, scanning logs, summarising large tool output. Use &lt;span class="sb"&gt;`Explore`&lt;/span&gt; type. |
| &lt;span class="sb"&gt;`sonnet`&lt;/span&gt; | Implementation from a precise spec: code and Terraform/HCL edits, tests, boilerplate, doc and PR-body drafting, applying review fixes. |
| &lt;span class="sb"&gt;`opus`&lt;/span&gt; | Implementation where the path is unclear: multi-file refactors, debugging with unknown cause, work needing judgement mid-task. |
| Fable (self) | Planning, architecture and design decisions, requirement clarification, code review, anything security- or prod-impacting, final approval and commit. |

Never use &lt;span class="sb"&gt;`subagent_type: "fork"`&lt;/span&gt; for grunt work. A fork inherits the full Fable context and always runs on Fable.

&lt;span class="gu"&gt;### Do it yourself when delegation costs more&lt;/span&gt;
Delegation has fixed overhead: writing the brief, reading the report, reviewing the diff. Do the work directly when:
&lt;span class="p"&gt;-&lt;/span&gt; the change is small (roughly under 20 lines or a single file edit),
&lt;span class="p"&gt;-&lt;/span&gt; it is a single-fact lookup and the file is already known,
&lt;span class="p"&gt;-&lt;/span&gt; the brief would be longer than the work itself,
&lt;span class="p"&gt;-&lt;/span&gt; the task is conversation, advice or a decision rather than production of code.

&lt;span class="gu"&gt;### Briefing standard&lt;/span&gt;
A subagent gets one precise brief, not a conversation. Every brief includes: exact files and scope, the required approach and conventions, what "done" looks like, what to leave untouched, and the report format (summary of changes plus anything uncertain). A vague brief produces a round-trip, and every round-trip re-bills the brief.

&lt;span class="gu"&gt;### Review and correction&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Fable reviews every diff a subagent produces before anything is committed. Read the actual diff, not just the agent's summary.
&lt;span class="p"&gt;-&lt;/span&gt; Deviation from the spec is corrected, always. If the fix is a few lines, Fable applies it directly. If the fix is substantial, send it back to the same agent via &lt;span class="sb"&gt;`SendMessage`&lt;/span&gt; (keeps its context) with the exact change required. Do not start a fresh agent to fix a running agent's work.
&lt;span class="p"&gt;-&lt;/span&gt; Fable commits, opens PRs and posts review requests. Subagents never commit or push.

&lt;span class="gu"&gt;### Parallelism&lt;/span&gt;
Independent tasks go to separate agents in one message so they run concurrently. Dependent tasks run sequentially through Fable, never chained agent to agent.

&lt;span class="gu"&gt;### Skills that dispatch agents&lt;/span&gt;
Superpowers (subagent-driven-development, dispatching-parallel-agents, requesting-code-review) and any other skill that spawns agents does so without a model. When following such a skill, add &lt;span class="sb"&gt;`model:`&lt;/span&gt; to every Agent call it prescribes: &lt;span class="sb"&gt;`sonnet`&lt;/span&gt; for implementers and spec-compliance reviewers, &lt;span class="sb"&gt;`haiku`&lt;/span&gt; for search. Mechanical review (does the output match the written spec) goes to a Sonnet agent; the judgement layer (is the design right, do the findings matter) stays with Fable in the main context so the same files are not read twice at Fable rates.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The three that matter most
&lt;/h2&gt;

&lt;p&gt;If you only take three things from that block:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Name a model on every agent call.&lt;/strong&gt; Without it, delegation saves nothing. I also set &lt;code&gt;CLAUDE_CODE_SUBAGENT_MODEL&lt;/code&gt; to &lt;code&gt;sonnet&lt;/code&gt; in &lt;code&gt;~/.claude/settings.json&lt;/code&gt; as a safety net for the calls I forget.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do it yourself when delegation costs more.&lt;/strong&gt; A brief, a report and a review are fixed overhead. Under about twenty lines, the brain does the work directly. Without this rule a multi-agent setup pays twice for one-line changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review the diff, not the summary.&lt;/strong&gt; Summaries are where "bumped the module version" hides "and reformatted 400 lines". Fixes go back to the same agent so its context is not paid for twice.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;p&gt;The expensive model now touches a task a few times, for a few minutes of judgement each. Search and edits run on models that cost a fraction of it, often in parallel, so tasks finish sooner as well as cheaper.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Lets Encrypt DNS Challenge with Traefik and AWS Route 53</title>
      <dc:creator>Kasun de Silva</dc:creator>
      <pubDate>Mon, 25 May 2026 04:20:01 +0000</pubDate>
      <link>https://dev.to/kasundsilva/lets-encrypt-dns-challenge-with-traefik-and-aws-route-53-36ak</link>
      <guid>https://dev.to/kasundsilva/lets-encrypt-dns-challenge-with-traefik-and-aws-route-53-36ak</guid>
      <description>&lt;p&gt;So, you're self-hosting awesome apps like Jellyfin, Home Assistant, or your personal blog with Docker. You want that sweet, sweet HTTPS padlock for secure connections, and Let's Encrypt is the obvious choice for free SSL certs. Awesome!&lt;/p&gt;

&lt;p&gt;You set up your reverse proxy (maybe Traefik, because it's slick!), point it to your app, and tell it to get a certificate... only to hit a wall. Why? Meet the home networker's nemesis: &lt;strong&gt;ISPs blocking incoming port 80.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Standard Way (and the Wall)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's Encrypt's default validation method, &lt;code&gt;HTTP-01&lt;/code&gt;, is simple: their servers try to access a special file on &lt;em&gt;your&lt;/em&gt; server over standard HTTP (port &lt;code&gt;80&lt;/code&gt;) to prove you control the domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's Encrypt -&amp;gt; Your Public IP:80 -&amp;gt; Does challenge file exist? -&amp;gt; OK! Cert Issued!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if your ISP blocks incoming connections on port &lt;code&gt;80&lt;/code&gt; (super common on residential plans!), Let's Encrypt's request never reaches your server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's Encrypt -&amp;gt; Your Public IP:80 -&amp;gt; [ISP BLOCK] -&amp;gt; Challenge Failed! No Cert! :(
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frustrating, right? Your quest for HTTPS seems doomed... or is it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The DNS-01 Challenge: A Different Kind of Proof&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enter the &lt;code&gt;DNS-01&lt;/code&gt; challenge – Let's Encrypt's clever workaround. Instead of checking a file via HTTP, it asks you to prove domain ownership by creating a specific &lt;strong&gt;DNS TXT record&lt;/strong&gt; with a unique value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's Encrypt -&amp;gt; Checks Your Domain's DNS Records -&amp;gt; Does specific TXT record exist? -&amp;gt; OK! Cert Issued!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since this validation happens entirely via DNS lookups (usually port &lt;code&gt;53&lt;/code&gt;, which is almost never blocked inbound for lookups), it completely bypasses the port &lt;code&gt;80&lt;/code&gt; problem!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Magic Combo: Traefik + AWS Route 53&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Okay, manually creating DNS records every ~90 days sounds tedious. This is where the dynamic duo comes in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Traefik:&lt;/strong&gt; Our smart reverse proxy knows how to talk the ACME protocol and request certificates using the DNS-01 method.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;AWS Route 53:&lt;/strong&gt; A robust, API-driven DNS service.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You configure Traefik with AWS API credentials (using an IAM user with &lt;em&gt;just enough&lt;/em&gt; permission to modify Route 53 records – security first!). When it's time to issue or renew a cert, Traefik tells Route 53 (via the API) to create the temporary TXT record Let's Encrypt needs. Once Let's Encrypt verifies it, Traefik tells Route 53 to clean it up. &lt;em&gt;Chef's kiss!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Key Configuration (Traefik + Docker Compose):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your Traefik service definition within &lt;code&gt;docker-compose.yml&lt;/code&gt;, the magic lies in the command arguments for your certificate resolver:&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;# Snippet from docker-compose.yml command section for Traefik&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ... other args ...&lt;/span&gt;
  &lt;span class="c1"&gt;# Let's Encrypt Resolver Config&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.email=your-email@example.com"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"&lt;/span&gt;
  &lt;span class="c1"&gt;# --- THE IMPORTANT PART ---&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.dnschallenge=true"&lt;/span&gt;          &lt;span class="c1"&gt;# Enable DNS Challenge&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.dnschallenge.provider=route53"&lt;/span&gt; &lt;span class="c1"&gt;# Tell it to use AWS Route 53&lt;/span&gt;
  &lt;span class="c1"&gt;# ... other args ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need to securely provide your AWS credentials (Access Key ID &amp;amp; Secret) as environment variables, preferably using a &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion: Problem Solved!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By switching to the DNS-01 challenge and leveraging the automation power of Traefik integrated with the AWS Route 53 API, the infamous port &lt;code&gt;80&lt;/code&gt; block becomes irrelevant for getting your Let's Encrypt certificates. You get fully automated, secure HTTPS for your self-hosted services without hassle. Now &lt;em&gt;that's&lt;/em&gt; cool.&lt;/p&gt;

&lt;p&gt;Happy (secure) self-hosting!&lt;/p&gt;




</description>
      <category>route53</category>
      <category>dns01</category>
      <category>letsencrypt</category>
      <category>dns</category>
    </item>
    <item>
      <title>Simplify AWS Lambda Deployments Using GitHub Actions</title>
      <dc:creator>Kasun de Silva</dc:creator>
      <pubDate>Fri, 15 Aug 2025 00:49:44 +0000</pubDate>
      <link>https://dev.to/aws-builders/simplify-aws-lambda-deployments-using-github-actions-43om</link>
      <guid>https://dev.to/aws-builders/simplify-aws-lambda-deployments-using-github-actions-43om</guid>
      <description>&lt;p&gt;Hey, serverless builders! 🚀 Exciting news from AWS!&lt;/p&gt;

&lt;p&gt;AWS has launched a feature that direct support for deploying AWS Lambda functions using GitHub Actions. This new capability significantly streamlines the deployment process, eliminating the need for complex, custom scripting and boilerplate code.&lt;/p&gt;

&lt;p&gt;Before this, deploying a Lambda function from a GitHub workflow required manual steps to package code, configure IAM roles, and handle potential errors. Now, a dedicated GitHub Action handles all of this for you with a simple, declarative YAML configuration. This means less friction, faster deployments, and more time for you to focus on building amazing serverless applications.&lt;/p&gt;




&lt;h3&gt;
  
  
  What's New?
&lt;/h3&gt;

&lt;p&gt;The new &lt;strong&gt;"Deploy Lambda Function" GitHub Action&lt;/strong&gt; simplifies your CI/CD pipeline by providing a direct and secure way to update your Lambda functions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declarative Configuration:&lt;/strong&gt; Define your deployment settings—like runtime, memory, and environment variables—directly in your GitHub Actions workflow file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Packaging:&lt;/strong&gt; The action automatically handles the packaging of your function code, supporting both &lt;code&gt;.zip&lt;/code&gt; file and container image deployments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless IAM Integration:&lt;/strong&gt; It integrates with AWS IAM using &lt;strong&gt;OpenID Connect (OIDC)&lt;/strong&gt; authentication, which is the most secure way to grant your GitHub workflows temporary, short-lived credentials without ever storing long-lived secrets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This new workflow is a huge win for developer experience, making it easier than ever to adopt a fully automated, Git-based deployment strategy for your serverless projects.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step-by-Step Guide: Deploying a Lambda Function with GitHub Actions
&lt;/h3&gt;

&lt;p&gt;Ready to get started? Here's how you can set up a GitHub Actions workflow to automatically deploy your Lambda function.&lt;/p&gt;

&lt;h4&gt;
  
  
  Prerequisites
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;A Lambda Function:&lt;/strong&gt; You need an existing AWS Lambda function. If you don't have one, create it in the AWS Management Console or with the AWS CLI.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;IAM Role for OIDC:&lt;/strong&gt; Configure an IAM role in your AWS account that trusts GitHub's OIDC provider. This role will grant your workflow the permissions it needs to deploy the function. This is a crucial security step!&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;A GitHub Repository:&lt;/strong&gt; Your Lambda function code should be in a GitHub repository.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Step 1: Configure IAM for OIDC
&lt;/h4&gt;

&lt;p&gt;First, set up a trusted relationship between your AWS account and GitHub.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;strong&gt;IAM&lt;/strong&gt; in the AWS console.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Access management&lt;/strong&gt;, select &lt;strong&gt;Identity providers&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Add provider&lt;/strong&gt; and configure an &lt;strong&gt;OpenID Connect&lt;/strong&gt; provider with the URL: &lt;code&gt;https://token.actions.githubusercontent.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a new IAM role that uses this provider and grant it the necessary permissions, such as &lt;code&gt;lambda:UpdateFunctionCode&lt;/code&gt; and &lt;code&gt;lambda:UpdateFunctionConfiguration&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 2: Create Your GitHub Actions Workflow File
&lt;/h4&gt;

&lt;p&gt;In your GitHub repository, create a new file at &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;. This YAML file defines the deployment process.&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Lambda Function&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&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="s"&gt;main&lt;/span&gt; &lt;span class="c1"&gt;# This workflow runs on pushes to the 'main' branch&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&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;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt; &lt;span class="c1"&gt;# Required for OIDC authentication&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;  &lt;span class="c1"&gt;# Required to check out the repository&lt;/span&gt;

    &lt;span class="na"&gt;steps&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;Checkout repository&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="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;Configure AWS credentials&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;aws-actions/configure-aws-credentials@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;role-to-assume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::123456789012:role/GitHubActionRole&lt;/span&gt; &lt;span class="c1"&gt;# Replace with your IAM role ARN&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt; &lt;span class="c1"&gt;# Replace with your AWS region&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;Deploy Lambda Function&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;aws-actions/aws-lambda-deploy@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;function-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-lambda-function&lt;/span&gt; &lt;span class="c1"&gt;# Replace with your function name&lt;/span&gt;
          &lt;span class="na"&gt;code-artifacts-dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./dist&lt;/span&gt; &lt;span class="c1"&gt;# The directory containing your packaged code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 3: Push Your Code and Watch it Deploy!
&lt;/h4&gt;

&lt;p&gt;That's it! When you push new code to the &lt;code&gt;main&lt;/code&gt; branch of your repository, GitHub Actions will automatically trigger this workflow. The &lt;code&gt;aws-lambda-deploy&lt;/code&gt; action will package the code from your specified directory (&lt;code&gt;./dist&lt;/code&gt; in this example) and deploy it to your Lambda function.&lt;/p&gt;




&lt;h3&gt;
  
  
  Beyond the Basics
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;aws-lambda-deploy&lt;/code&gt; action is highly configurable. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deploy via Amazon S3:&lt;/strong&gt; For larger deployment packages, you can specify an S3 bucket to use as an intermediate location.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure Function Settings:&lt;/strong&gt; Update your Lambda function's runtime, memory, timeout, and environment variables directly within the workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Dry Run Mode:&lt;/strong&gt; Test your deployment configuration and permissions without making any changes to the function itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this new feature, AWS is making it simpler and more secure to manage the entire lifecycle of your serverless applications. Happy coding! 💻✨&lt;/p&gt;

&lt;p&gt;For more details and advanced examples, check out the &lt;strong&gt;AWS Lambda Deploy GitHub Action&lt;/strong&gt; repository: &lt;a href="https://github.com/aws-actions/aws-lambda-deploy" rel="noopener noreferrer"&gt;https://github.com/aws-actions/aws-lambda-deploy&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>lambda</category>
      <category>githubactions</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Lets Encrypt DNS Challenge with Traefik and AWS Route 53</title>
      <dc:creator>Kasun de Silva</dc:creator>
      <pubDate>Fri, 15 Aug 2025 00:46:58 +0000</pubDate>
      <link>https://dev.to/kasundsilva/lets-encrypt-dns-challenge-with-traefik-and-aws-route-53-1312</link>
      <guid>https://dev.to/kasundsilva/lets-encrypt-dns-challenge-with-traefik-and-aws-route-53-1312</guid>
      <description>&lt;p&gt;So, you're self-hosting awesome apps like Jellyfin, Home Assistant, or your personal blog with Docker. You want that sweet, sweet HTTPS padlock for secure connections, and Let's Encrypt is the obvious choice for free SSL certs. Awesome!&lt;/p&gt;

&lt;p&gt;You set up your reverse proxy (maybe Traefik, because it's slick!), point it to your app, and tell it to get a certificate... only to hit a wall. Why? Meet the home networker's nemesis: &lt;strong&gt;ISPs blocking incoming port 80.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Standard Way (and the Wall)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's Encrypt's default validation method, &lt;code&gt;HTTP-01&lt;/code&gt;, is simple: their servers try to access a special file on &lt;em&gt;your&lt;/em&gt; server over standard HTTP (port &lt;code&gt;80&lt;/code&gt;) to prove you control the domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's Encrypt -&amp;gt; Your Public IP:80 -&amp;gt; Does challenge file exist? -&amp;gt; OK! Cert Issued!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if your ISP blocks incoming connections on port &lt;code&gt;80&lt;/code&gt; (super common on residential plans!), Let's Encrypt's request never reaches your server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's Encrypt -&amp;gt; Your Public IP:80 -&amp;gt; [ISP BLOCK] -&amp;gt; Challenge Failed! No Cert! :(
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frustrating, right? Your quest for HTTPS seems doomed... or is it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The DNS-01 Challenge: A Different Kind of Proof&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enter the &lt;code&gt;DNS-01&lt;/code&gt; challenge – Let's Encrypt's clever workaround. Instead of checking a file via HTTP, it asks you to prove domain ownership by creating a specific &lt;strong&gt;DNS TXT record&lt;/strong&gt; with a unique value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's Encrypt -&amp;gt; Checks Your Domain's DNS Records -&amp;gt; Does specific TXT record exist? -&amp;gt; OK! Cert Issued!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since this validation happens entirely via DNS lookups (usually port &lt;code&gt;53&lt;/code&gt;, which is almost never blocked inbound for lookups), it completely bypasses the port &lt;code&gt;80&lt;/code&gt; problem!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Magic Combo: Traefik + AWS Route 53&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Okay, manually creating DNS records every ~90 days sounds tedious. This is where the dynamic duo comes in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Traefik:&lt;/strong&gt; Our smart reverse proxy knows how to talk the ACME protocol and request certificates using the DNS-01 method.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;AWS Route 53:&lt;/strong&gt; A robust, API-driven DNS service.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You configure Traefik with AWS API credentials (using an IAM user with &lt;em&gt;just enough&lt;/em&gt; permission to modify Route 53 records – security first!). When it's time to issue or renew a cert, Traefik tells Route 53 (via the API) to create the temporary TXT record Let's Encrypt needs. Once Let's Encrypt verifies it, Traefik tells Route 53 to clean it up. &lt;em&gt;Chef's kiss!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Key Configuration (Traefik + Docker Compose):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your Traefik service definition within &lt;code&gt;docker-compose.yml&lt;/code&gt;, the magic lies in the command arguments for your certificate resolver:&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;# Snippet from docker-compose.yml command section for Traefik&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ... other args ...&lt;/span&gt;
  &lt;span class="c1"&gt;# Let's Encrypt Resolver Config&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.email=your-email@example.com"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"&lt;/span&gt;
  &lt;span class="c1"&gt;# --- THE IMPORTANT PART ---&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.dnschallenge=true"&lt;/span&gt;          &lt;span class="c1"&gt;# Enable DNS Challenge&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.dnschallenge.provider=route53"&lt;/span&gt; &lt;span class="c1"&gt;# Tell it to use AWS Route 53&lt;/span&gt;
  &lt;span class="c1"&gt;# ... other args ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need to securely provide your AWS credentials (Access Key ID &amp;amp; Secret) as environment variables, preferably using a &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion: Problem Solved!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By switching to the DNS-01 challenge and leveraging the automation power of Traefik integrated with the AWS Route 53 API, the infamous port &lt;code&gt;80&lt;/code&gt; block becomes irrelevant for getting your Let's Encrypt certificates. You get fully automated, secure HTTPS for your self-hosted services without hassle. Now &lt;em&gt;that's&lt;/em&gt; cool.&lt;/p&gt;

&lt;p&gt;Happy (secure) self-hosting!&lt;/p&gt;




</description>
      <category>route53</category>
      <category>dns01</category>
      <category>letsencrypt</category>
      <category>dns</category>
    </item>
    <item>
      <title>Let's Encrypt DNS Challenge with Traefik and AWS Route 53</title>
      <dc:creator>Kasun de Silva</dc:creator>
      <pubDate>Fri, 15 Aug 2025 00:46:30 +0000</pubDate>
      <link>https://dev.to/kasundsilva/lets-encrypt-dns-challenge-with-traefik-and-aws-route-53-3b5e</link>
      <guid>https://dev.to/kasundsilva/lets-encrypt-dns-challenge-with-traefik-and-aws-route-53-3b5e</guid>
      <description>&lt;p&gt;So, you're self-hosting awesome apps like Jellyfin, Home Assistant, or your personal blog with Docker. You want that sweet, sweet HTTPS padlock for secure connections, and Let's Encrypt is the obvious choice for free SSL certs. Awesome!&lt;/p&gt;

&lt;p&gt;You set up your reverse proxy (maybe Traefik, because it's slick!), point it to your app, and tell it to get a certificate... only to hit a wall. Why? Meet the home networker's nemesis: &lt;strong&gt;ISPs blocking incoming port 80.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Standard Way (and the Wall)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's Encrypt's default validation method, &lt;code&gt;HTTP-01&lt;/code&gt;, is simple: their servers try to access a special file on &lt;em&gt;your&lt;/em&gt; server over standard HTTP (port &lt;code&gt;80&lt;/code&gt;) to prove you control the domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's Encrypt -&amp;gt; Your Public IP:80 -&amp;gt; Does challenge file exist? -&amp;gt; OK! Cert Issued!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if your ISP blocks incoming connections on port &lt;code&gt;80&lt;/code&gt; (super common on residential plans!), Let's Encrypt's request never reaches your server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's Encrypt -&amp;gt; Your Public IP:80 -&amp;gt; [ISP BLOCK] -&amp;gt; Challenge Failed! No Cert! :(
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frustrating, right? Your quest for HTTPS seems doomed... or is it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The DNS-01 Challenge: A Different Kind of Proof&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enter the &lt;code&gt;DNS-01&lt;/code&gt; challenge – Let's Encrypt's clever workaround. Instead of checking a file via HTTP, it asks you to prove domain ownership by creating a specific &lt;strong&gt;DNS TXT record&lt;/strong&gt; with a unique value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's Encrypt -&amp;gt; Checks Your Domain's DNS Records -&amp;gt; Does specific TXT record exist? -&amp;gt; OK! Cert Issued!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since this validation happens entirely via DNS lookups (usually port &lt;code&gt;53&lt;/code&gt;, which is almost never blocked inbound for lookups), it completely bypasses the port &lt;code&gt;80&lt;/code&gt; problem!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Magic Combo: Traefik + AWS Route 53&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Okay, manually creating DNS records every ~90 days sounds tedious. This is where the dynamic duo comes in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Traefik:&lt;/strong&gt; Our smart reverse proxy knows how to talk the ACME protocol and request certificates using the DNS-01 method.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;AWS Route 53:&lt;/strong&gt; A robust, API-driven DNS service.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You configure Traefik with AWS API credentials (using an IAM user with &lt;em&gt;just enough&lt;/em&gt; permission to modify Route 53 records – security first!). When it's time to issue or renew a cert, Traefik tells Route 53 (via the API) to create the temporary TXT record Let's Encrypt needs. Once Let's Encrypt verifies it, Traefik tells Route 53 to clean it up. &lt;em&gt;Chef's kiss!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Key Configuration (Traefik + Docker Compose):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your Traefik service definition within &lt;code&gt;docker-compose.yml&lt;/code&gt;, the magic lies in the command arguments for your certificate resolver:&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;# Snippet from docker-compose.yml command section for Traefik&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ... other args ...&lt;/span&gt;
  &lt;span class="c1"&gt;# Let's Encrypt Resolver Config&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.email=your-email@example.com"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"&lt;/span&gt;
  &lt;span class="c1"&gt;# --- THE IMPORTANT PART ---&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.dnschallenge=true"&lt;/span&gt;          &lt;span class="c1"&gt;# Enable DNS Challenge&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.dnschallenge.provider=route53"&lt;/span&gt; &lt;span class="c1"&gt;# Tell it to use AWS Route 53&lt;/span&gt;
  &lt;span class="c1"&gt;# ... other args ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need to securely provide your AWS credentials (Access Key ID &amp;amp; Secret) as environment variables, preferably using a &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion: Problem Solved!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By switching to the DNS-01 challenge and leveraging the automation power of Traefik integrated with the AWS Route 53 API, the infamous port &lt;code&gt;80&lt;/code&gt; block becomes irrelevant for getting your Let's Encrypt certificates. You get fully automated, secure HTTPS for your self-hosted services without hassle. Now &lt;em&gt;that's&lt;/em&gt; cool.&lt;/p&gt;

&lt;p&gt;Happy (secure) self-hosting!&lt;/p&gt;




</description>
      <category>route53</category>
      <category>dns01</category>
      <category>letsencrypt</category>
      <category>dns</category>
    </item>
    <item>
      <title>Let's Encrypt DNS-01 Challenge with Traefik and AWS Route 53</title>
      <dc:creator>Kasun de Silva</dc:creator>
      <pubDate>Tue, 06 May 2025 00:10:54 +0000</pubDate>
      <link>https://dev.to/aws-builders/lets-encrypt-dns-challenge-with-traefik-and-aws-route-53-1l1f</link>
      <guid>https://dev.to/aws-builders/lets-encrypt-dns-challenge-with-traefik-and-aws-route-53-1l1f</guid>
      <description>&lt;p&gt;So, you're self-hosting awesome apps like Jellyfin, Home Assistant, or your personal blog with Docker. You want that sweet, sweet HTTPS padlock for secure connections, and Let's Encrypt is the obvious choice for free SSL certs. Awesome!&lt;/p&gt;

&lt;p&gt;You set up your reverse proxy (maybe Traefik, because it's slick!), point it to your app, and tell it to get a certificate... only to hit a wall. Why? Meet the home networker's nemesis: &lt;strong&gt;ISPs blocking incoming port 80.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Standard Way (and the Wall)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's Encrypt's default validation method, &lt;code&gt;HTTP-01&lt;/code&gt;, is simple: their servers try to access a special file on &lt;em&gt;your&lt;/em&gt; server over standard HTTP (port &lt;code&gt;80&lt;/code&gt;) to prove you control the domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's Encrypt -&amp;gt; Your Public IP:80 -&amp;gt; Does challenge file exist? -&amp;gt; OK! Cert Issued!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if your ISP blocks incoming connections on port &lt;code&gt;80&lt;/code&gt; (super common on residential plans!), Let's Encrypt's request never reaches your server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's Encrypt -&amp;gt; Your Public IP:80 -&amp;gt; [ISP BLOCK] -&amp;gt; Challenge Failed! No Cert! :(
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frustrating, right? Your quest for HTTPS seems doomed... or is it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The DNS-01 Challenge: A Different Kind of Proof&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enter the &lt;code&gt;DNS-01&lt;/code&gt; challenge – Let's Encrypt's clever workaround. Instead of checking a file via HTTP, it asks you to prove domain ownership by creating a specific &lt;strong&gt;DNS TXT record&lt;/strong&gt; with a unique value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's Encrypt -&amp;gt; Checks Your Domain's DNS Records -&amp;gt; Does specific TXT record exist? -&amp;gt; OK! Cert Issued!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since this validation happens entirely via DNS lookups (usually port &lt;code&gt;53&lt;/code&gt;, which is almost never blocked inbound for lookups), it completely bypasses the port &lt;code&gt;80&lt;/code&gt; problem!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Magic Combo: Traefik + AWS Route 53&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Okay, manually creating DNS records every ~90 days sounds tedious. This is where the dynamic duo comes in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Traefik:&lt;/strong&gt; Our smart reverse proxy knows how to talk the ACME protocol and request certificates using the DNS-01 method.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;AWS Route 53:&lt;/strong&gt; A robust, API-driven DNS service.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You configure Traefik with AWS API credentials (using an IAM user with &lt;em&gt;just enough&lt;/em&gt; permission to modify Route 53 records – security first!). When it's time to issue or renew a cert, Traefik tells Route 53 (via the API) to create the temporary TXT record Let's Encrypt needs. Once Let's Encrypt verifies it, Traefik tells Route 53 to clean it up. &lt;em&gt;Chef's kiss!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Key Configuration (Traefik + Docker Compose):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your Traefik service definition within &lt;code&gt;docker-compose.yml&lt;/code&gt;, the magic lies in the command arguments for your certificate resolver:&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;# Snippet from docker-compose.yml command section for Traefik&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ... other args ...&lt;/span&gt;
  &lt;span class="c1"&gt;# Let's Encrypt Resolver Config&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.email=your-email@example.com"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"&lt;/span&gt;
  &lt;span class="c1"&gt;# --- THE IMPORTANT PART ---&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.dnschallenge=true"&lt;/span&gt;          &lt;span class="c1"&gt;# Enable DNS Challenge&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.dnschallenge.provider=route53"&lt;/span&gt; &lt;span class="c1"&gt;# Tell it to use AWS Route 53&lt;/span&gt;
  &lt;span class="c1"&gt;# ... other args ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Environment Vars&lt;/strong&gt;&lt;br&gt;
You also need to securely provide your AWS credentials (Access Key ID &amp;amp; Secret) as environment variables, preferably using a &lt;code&gt;.env&lt;/code&gt; file.&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;# Environment variables for AWS credentials (will use .env file)&lt;/span&gt;
&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_REGION&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_HOSTED_ZONE_ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Complete example config,&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;# docker-compose.yml&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Traefik Service (Reverse Proxy &amp;amp; SSL)&lt;/span&gt;
  &lt;span class="na"&gt;traefik&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik:v2.11"&lt;/span&gt; &lt;span class="c1"&gt;# Pin to a specific stable version&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik-proxy"&lt;/span&gt; &lt;span class="c1"&gt;# Descriptive container name&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable Docker provider &amp;amp; disable exposing by default&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--providers.docker=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--providers.docker.exposedbydefault=false"&lt;/span&gt;
      &lt;span class="c1"&gt;# Define HTTP (80) and HTTPS (443) entry points&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.web.address=:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.websecure.address=:443"&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable Traefik API/Dashboard (for monitoring)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--api.dashboard=true"&lt;/span&gt;
      &lt;span class="c1"&gt;# Configure Let's Encrypt Resolver (named 'myresolver')&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.email=your-email@example.com"&lt;/span&gt; &lt;span class="c1"&gt;# !! REPLACE THIS with your email !!&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"&lt;/span&gt;
      &lt;span class="c1"&gt;# Use DNS-01 challenge with Route53 provider&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.dnschallenge=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.dnschallenge.provider=route53"&lt;/span&gt;
      &lt;span class="c1"&gt;# Global Redirect: HTTP -&amp;gt; HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.web.http.redirections.entrypoint.to=websecure"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.web.http.redirections.entrypoint.scheme=https"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Expose port 80 for HTTP-&amp;gt;HTTPS redirect (optional if ISP blocks)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="c1"&gt;# Expose port 443 for HTTPS access (ESSENTIAL)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Mount Docker socket to detect container events&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock:ro"&lt;/span&gt;
      &lt;span class="c1"&gt;# Persist Let's Encrypt certificates&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./letsencrypt:/letsencrypt"&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxy&lt;/span&gt; &lt;span class="c1"&gt;# Connect to our custom network&lt;/span&gt;
    &lt;span class="c1"&gt;# Environment variables for AWS credentials (will use .env file)&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_REGION&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_HOSTED_ZONE_ID&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable Traefik for its own dashboard&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.enable=true"&lt;/span&gt;
      &lt;span class="c1"&gt;# Dashboard Router Rule (HTTPS)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.traefik-dashboard.rule=Host(`traefik.yourdomain.com`)"&lt;/span&gt; &lt;span class="c1"&gt;# !! REPLACE with your dashboard hostname !!&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.traefik-dashboard.entrypoints=websecure"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.traefik-dashboard.service=api@internal"&lt;/span&gt;
      &lt;span class="c1"&gt;# Use Let's Encrypt for the dashboard domain&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.traefik-dashboard.tls.certresolver=myresolver"&lt;/span&gt;
      &lt;span class="c1"&gt;# Secure the dashboard with Basic Auth&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.traefik-dashboard.middlewares=auth"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.middlewares.auth.basicauth.users=user:$&amp;lt;span&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;class="math-inline"&amp;gt;apr1&amp;lt;/span&amp;gt;&amp;lt;span class="math-inline"&amp;gt;abcdefg&amp;lt;/span&amp;gt;$yourhashedpassword"&lt;/span&gt; &lt;span class="c1"&gt;# !! REPLACE THIS HASH !!&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Conclusion: Problem Solved!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By switching to the DNS-01 challenge and leveraging the automation power of Traefik integrated with the AWS Route 53 API, the infamous port &lt;code&gt;80&lt;/code&gt; block becomes irrelevant for getting your Let's Encrypt certificates. You get fully automated, secure HTTPS for your self-hosted services without hassle. Now &lt;em&gt;that's&lt;/em&gt; cool.&lt;/p&gt;

&lt;p&gt;Happy (secure) self-hosting!&lt;/p&gt;




</description>
      <category>route53</category>
      <category>letsencrypt</category>
      <category>dns</category>
      <category>traefik</category>
    </item>
    <item>
      <title>From Plex to Freedom: My Jellyfin Journey with Traefik &amp; AWS Route 53</title>
      <dc:creator>Kasun de Silva</dc:creator>
      <pubDate>Mon, 05 May 2025 08:20:13 +0000</pubDate>
      <link>https://dev.to/kasundsilva/from-plex-to-freedom-my-jellyfin-journey-with-traefik-aws-route-53-4hgb</link>
      <guid>https://dev.to/kasundsilva/from-plex-to-freedom-my-jellyfin-journey-with-traefik-aws-route-53-4hgb</guid>
      <description>&lt;p&gt;Like many, I relied on &lt;strong&gt;Plex&lt;/strong&gt; for years to manage and stream my personal media library – hundreds of movies and countless TV show episodes. It served me well. However, recent shifts towards monetizing core features, like remote streaming, prompted me to look for alternatives. I wanted a solution that was open-source, powerful, and gave me complete control over my media and its access, without unexpected subscription requirements for basic functionality.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;Jellyfin&lt;/strong&gt;. It's a fantastic, free software media system that puts you in control. But simply installing it isn't enough; I needed secure, easy remote access for myself and my family, just like I had before.&lt;/p&gt;

&lt;p&gt;This post details my journey setting up Jellyfin locally using &lt;strong&gt;Docker&lt;/strong&gt;, but making it securely accessible from anywhere using &lt;strong&gt;Traefik&lt;/strong&gt; as a reverse proxy, &lt;strong&gt;Let's Encrypt&lt;/strong&gt; for free SSL certificates, and leveraging &lt;strong&gt;AWS Route 53&lt;/strong&gt; for seamless and reliable DNS hosting. If you're comfortable with Docker and use AWS for your DNS, this guide is for you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Stack?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jellyfin:&lt;/strong&gt; Awesome open-source media server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker &amp;amp; Docker Compose:&lt;/strong&gt; Easy deployment, management, and reproducibility of services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traefik:&lt;/strong&gt; A modern, cloud-native reverse proxy that integrates beautifully with Docker. It automatically discovers services and handles SSL certificate management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let's Encrypt:&lt;/strong&gt; Free, automated SSL certificates. Essential for secure HTTPS connections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Route 53:&lt;/strong&gt; Reliable DNS hosting. Crucially, its API allows Traefik to perform the Let's Encrypt DNS-01 challenge automatically – perfect if your ISP blocks port 80 (a common issue!).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;A server or machine (like a NAS, Raspberry Pi, or home server) running &lt;strong&gt;Docker&lt;/strong&gt; and &lt;strong&gt;Docker Compose&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A registered domain name (we'll use &lt;code&gt;yourdomain.com&lt;/code&gt; as an example).&lt;/li&gt;
&lt;li&gt;Your domain's DNS hosted on &lt;strong&gt;AWS Route 53&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;AWS account&lt;/strong&gt; with IAM permissions to modify Route 53 records for &lt;code&gt;yourdomain.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Basic familiarity with the command line.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Challenge: Secure Remote Access &amp;amp; The Port 80 Problem
&lt;/h2&gt;

&lt;p&gt;Running Jellyfin locally is easy, but accessing it securely from outside your network requires &lt;strong&gt;HTTPS&lt;/strong&gt;. Let's Encrypt provides free SSL certificates via the &lt;strong&gt;ACME protocol&lt;/strong&gt;. The most common validation method (HTTP-01 challenge) requires your server to be reachable from the internet on port &lt;code&gt;80&lt;/code&gt;. Unfortunately, many residential ISPs block incoming traffic on port &lt;code&gt;80&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is where the &lt;strong&gt;DNS-01 challenge&lt;/strong&gt; shines. Instead of checking port &lt;code&gt;80&lt;/code&gt;, Let's Encrypt verifies domain ownership by checking for a specific TXT record in your DNS zone. Since we're using Route 53, Traefik can automatically create and remove these temporary records using the AWS API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: AWS Setup - IAM User for Traefik
&lt;/h2&gt;

&lt;p&gt;Traefik needs permission to modify your Route 53 DNS records for &lt;code&gt;yourdomain.com&lt;/code&gt;. We'll create a dedicated IAM user with &lt;em&gt;only&lt;/em&gt; the necessary permissions (Principle of Least Privilege).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Go to the &lt;strong&gt;IAM console&lt;/strong&gt; in AWS.&lt;/li&gt;
&lt;li&gt; Create a new &lt;strong&gt;User&lt;/strong&gt;. Give it a descriptive name (e.g., &lt;code&gt;traefik-route53-manager&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Select "&lt;strong&gt;Attach policies directly&lt;/strong&gt;".&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;strong&gt;new policy&lt;/strong&gt;. Use the JSON editor and paste the following policy, replacing &lt;code&gt;YOUR_HOSTED_ZONE_ID&lt;/code&gt; with the actual Route 53 Hosted Zone ID for &lt;code&gt;yourdomain.com&lt;/code&gt; (you can find this in the Route 53 console):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"route53:GetChange"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"route53:ListHostedZonesByName"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"route53:ChangeResourceRecordSets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"route53:ListResourceRecordSets"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:route53:::hostedzone/YOUR_HOSTED_ZONE_ID"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;em&gt;&amp;gt; Note: Using &lt;code&gt;ListHostedZonesByName&lt;/code&gt; and filtering by resource ARN for &lt;code&gt;ChangeResourceRecordSets&lt;/code&gt; scopes down permissions significantly.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finish creating the policy (give it a name like &lt;code&gt;TraefikRoute53AccessPolicy&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Attach this new policy to the user you created.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Proceed to the "&lt;strong&gt;Security credentials&lt;/strong&gt;" tab for the new user.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create an "&lt;strong&gt;Access key&lt;/strong&gt;" - select "&lt;strong&gt;Application running outside AWS&lt;/strong&gt;" as the use case.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Carefully copy the Access Key ID and Secret Access Key.&lt;/strong&gt; You won't see the secret key again! Store these securely for the next steps.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2: Docker Compose Configuration
&lt;/h2&gt;

&lt;p&gt;Now, let's define our services using &lt;code&gt;docker-compose.yml&lt;/code&gt;. This file defines the Traefik reverse proxy and the Jellyfin application itself. Remember to replace the example hostnames (&lt;code&gt;jellyfin.yourdomain.com&lt;/code&gt;, &lt;code&gt;traefik.yourdomain.com&lt;/code&gt;) with your actual desired subdomains.&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;# docker-compose.yml&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Traefik Service (Reverse Proxy &amp;amp; SSL)&lt;/span&gt;
  &lt;span class="na"&gt;traefik&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik:v2.11"&lt;/span&gt; &lt;span class="c1"&gt;# Pin to a specific stable version&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik-proxy"&lt;/span&gt; &lt;span class="c1"&gt;# Descriptive container name&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable Docker provider &amp;amp; disable exposing by default&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--providers.docker=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--providers.docker.exposedbydefault=false"&lt;/span&gt;
      &lt;span class="c1"&gt;# Define HTTP (80) and HTTPS (443) entry points&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.web.address=:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.websecure.address=:443"&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable Traefik API/Dashboard (for monitoring)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--api.dashboard=true"&lt;/span&gt;
      &lt;span class="c1"&gt;# Configure Let's Encrypt Resolver (named 'myresolver')&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.email=your-email@example.com"&lt;/span&gt; &lt;span class="c1"&gt;# !! REPLACE THIS with your email !!&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"&lt;/span&gt;
      &lt;span class="c1"&gt;# Use DNS-01 challenge with Route53 provider&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.dnschallenge=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.dnschallenge.provider=route53"&lt;/span&gt;
      &lt;span class="c1"&gt;# Global Redirect: HTTP -&amp;gt; HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.web.http.redirections.entrypoint.to=websecure"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.web.http.redirections.entrypoint.scheme=https"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Expose port 80 for HTTP-&amp;gt;HTTPS redirect (optional if ISP blocks)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="c1"&gt;# Expose port 443 for HTTPS access (ESSENTIAL)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Mount Docker socket to detect container events&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock:ro"&lt;/span&gt;
      &lt;span class="c1"&gt;# Persist Let's Encrypt certificates&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./letsencrypt:/letsencrypt"&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxy&lt;/span&gt; &lt;span class="c1"&gt;# Connect to our custom network&lt;/span&gt;
    &lt;span class="c1"&gt;# Environment variables for AWS credentials (will use .env file)&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;
      &lt;span class="c1"&gt;# - AWS_REGION=us-east-1 # Optional: Specify if needed&lt;/span&gt;
      &lt;span class="c1"&gt;# - AWS_HOSTED_ZONE_ID=YOUR_HOSTED_ZONE_ID # Optional&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable Traefik for its own dashboard&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.enable=true"&lt;/span&gt;
      &lt;span class="c1"&gt;# Dashboard Router Rule (HTTPS)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.traefik-dashboard.rule=Host(`traefik.yourdomain.com`)"&lt;/span&gt; &lt;span class="c1"&gt;# !! REPLACE with your dashboard hostname !!&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.traefik-dashboard.entrypoints=websecure"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.traefik-dashboard.service=api@internal"&lt;/span&gt;
      &lt;span class="c1"&gt;# Use Let's Encrypt for the dashboard domain&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.traefik-dashboard.tls.certresolver=myresolver"&lt;/span&gt;
      &lt;span class="c1"&gt;# Secure the dashboard with Basic Auth&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.traefik-dashboard.middlewares=auth"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.middlewares.auth.basicauth.users=user:$&amp;lt;span&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;class="math-inline"&amp;gt;apr1&amp;lt;/span&amp;gt;&amp;lt;span class="math-inline"&amp;gt;abcdefg&amp;lt;/span&amp;gt;$yourhashedpassword"&lt;/span&gt; &lt;span class="c1"&gt;# !! REPLACE THIS HASH !!&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

  &lt;span class="c1"&gt;# Jellyfin Service&lt;/span&gt;
  &lt;span class="na"&gt;jellyfin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lscr.io/linuxserver/jellyfin:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jellyfin&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PUID=1000&lt;/span&gt; &lt;span class="c1"&gt;# Your user ID&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGID=1000&lt;/span&gt; &lt;span class="c1"&gt;# Your group ID&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;TZ=Etc/UTC&lt;/span&gt; &lt;span class="c1"&gt;# !! REPLACE with Your timezone e.g. Pacific/Auckland !!&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Map Jellyfin config directory&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/path/to/jellyfin/config:/config/&lt;/span&gt; &lt;span class="c1"&gt;# !! REPLACE host path to your config location !!&lt;/span&gt;
      &lt;span class="c1"&gt;# Map Media Libraries&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/path/to/tvshows:/data/tvshows&lt;/span&gt;   &lt;span class="c1"&gt;# !! REPLACE host path to your TV shows !!&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/path/to/movies:/data/movies&lt;/span&gt;     &lt;span class="c1"&gt;# !! REPLACE host path to your Movies !!&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxy&lt;/span&gt; &lt;span class="c1"&gt;# Connect to the same network as Traefik&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable Traefik management for this container&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.enable=true"&lt;/span&gt;
      &lt;span class="c1"&gt;# Jellyfin Router Rule (HTTPS)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.jellyfin.rule=Host(`jellyfin.yourdomain.com`)"&lt;/span&gt; &lt;span class="c1"&gt;# !! REPLACE with your Jellyfin hostname !!&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.jellyfin.entrypoints=websecure"&lt;/span&gt; &lt;span class="c1"&gt;# Use HTTPS entrypoint&lt;/span&gt;
      &lt;span class="c1"&gt;# Use Let's Encrypt resolver defined above&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.jellyfin.tls.certresolver=myresolver"&lt;/span&gt;
      &lt;span class="c1"&gt;# Tell Traefik which internal port Jellyfin uses&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.jellyfin.loadbalancer.server.port=8096"&lt;/span&gt;

&lt;span class="c1"&gt;# Define the shared network&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;proxy&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;proxy&lt;/span&gt;

&lt;span class="c1"&gt;# Define the volume for Let's Encrypt certificates&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;letsencrypt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Handling Secrets - The &lt;code&gt;.env&lt;/code&gt; File
&lt;/h2&gt;

&lt;p&gt;Never commit secrets directly into your &lt;code&gt;docker-compose.yml&lt;/code&gt;! We'll use a &lt;code&gt;.env&lt;/code&gt; file to store the AWS credentials.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;In the same directory as your &lt;code&gt;docker-compose.yml&lt;/code&gt;, create a file named &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add your AWS keys:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env
AWS_ACCESS_KEY_ID=YOUR_ACTUAL_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY=YOUR_ACTUAL_SECRET_ACCESS_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Crucially: Add .env to your .gitignore file to prevent accidentally committing it.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .gitignore
.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Docker Compose will automatically read this file and inject these variables into the Traefik container's environment, as specified in the environment: section of the &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Securing the Traefik Dashboard
&lt;/h2&gt;

&lt;p&gt;We added basic authentication to the dashboard using Traefik labels. You need to generate a password hash.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install htpasswd (usually via apache2-utils or httpd-tools).&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following, replacing admin and 'YourSecurePassword!' with your desired credentials:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;htpasswd &lt;span class="nt"&gt;-nb&lt;/span&gt; admin &lt;span class="s1"&gt;'YourSecurePassword!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the output (e.g., admin:$apr1$somehash$morehash).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Paste it into the &lt;code&gt;traefik.http.middlewares.auth.basicauth.users&lt;/code&gt; label in &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;br&gt;
Remember to escape the dollar signs &lt;code&gt;($)&lt;/code&gt; by doubling them &lt;code&gt;($$)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Example label in docker-compose.yml&lt;/span&gt;
&lt;span class="s"&gt;_ "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$somehash$$morehash"&lt;/span&gt; &lt;span class="c1"&gt;# !! REPLACE user:hash !!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 5: DNS Configuration (Route 53)
&lt;/h2&gt;

&lt;p&gt;Before starting, ensure you have A records in Route 53 pointing your chosen hostnames (e.g., &lt;code&gt;jellyfin.yourdomain.com&lt;/code&gt; and &lt;code&gt;traefik.yourdomain.com&lt;/code&gt;) to the public IP address of the server running Docker.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jellyfin.yourdomain.com&lt;/code&gt; -&amp;gt; &lt;code&gt;YOUR_SERVER_PUBLIC_IP&lt;/code&gt;&lt;br&gt;
&lt;code&gt;traefik.yourdomain.com&lt;/code&gt; -&amp;gt; &lt;code&gt;YOUR_SERVER_PUBLIC_IP&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Final Checks &amp;amp; Deployment
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Placeholders&lt;/strong&gt;: Ensure you've replaced all placeholders marked with &lt;code&gt;!! REPLACE !!&lt;/code&gt; or &lt;code&gt;YOUR_...&lt;/code&gt; in the &lt;code&gt;docker-compose.yml&lt;/code&gt; and &lt;code&gt;.env&lt;/code&gt; files with your actual values (hostnames, email, paths, user IDs, timezone, password hash, AWS keys).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paths&lt;/strong&gt;: Double-check the host paths mapped in the volumes section for Jellyfin – point them to your actual config and media library locations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User/Group ID&lt;/strong&gt;: Update &lt;code&gt;PUID&lt;/code&gt; and &lt;code&gt;PGID&lt;/code&gt; in the Jellyfin environment section to match the user/group that owns your media files (use id &lt;code&gt;$USER&lt;/code&gt; on &lt;code&gt;Linux&lt;/code&gt; to find yours).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Directory&lt;/strong&gt;: Create the Let's Encrypt volume directory: &lt;code&gt;mkdir ./letsencrypt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firewall/Router&lt;/strong&gt;: Ensure port &lt;code&gt;443&lt;/code&gt; is forwarded from your router/firewall to the internal IP address of your Docker host machine. Port &lt;code&gt;80&lt;/code&gt; forwarding is optional now but recommended for the &lt;code&gt;HTTP-&amp;gt;HTTPS&lt;/code&gt; redirect to work smoothly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Launch&lt;/strong&gt;: Run &lt;code&gt;docker compose up -d&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor&lt;/strong&gt;: Check the Traefik logs, especially on the first run, to ensure successful AWS authentication and certificate acquisition: &lt;code&gt;docker compose logs -f traefik-proxy&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;You should now have a fully functional Jellyfin instance accessible securely via &lt;code&gt;https://jellyfin.yourdomain.com&lt;/code&gt; (using your actual domain), with automatic SSL certificate management handled by Traefik and Let's Encrypt, leveraging the power and reliability of AWS Route 53 for DNS validation. The Traefik dashboard is also available (and password-protected) at &lt;code&gt;https://traefik.yourdomain.com&lt;/code&gt; (again, using your actual domain).&lt;/p&gt;

&lt;p&gt;This setup gives me the control and freedom I was seeking after moving from Plex. By combining powerful open-source tools like Jellyfin and Traefik with robust cloud services like AWS Route 53 and IAM, we can build secure, flexible, and non-subscription-based solutions tailored perfectly to our needs.&lt;/p&gt;

&lt;p&gt;Happy self-hosting! Let me know about your experiences in the comments below.&lt;/p&gt;

</description>
      <category>route53</category>
      <category>dns</category>
      <category>traefik</category>
      <category>jellyfin</category>
    </item>
    <item>
      <title>CodeBuild Meets GitHub Actions: A Serverless CI Workflow with Lambda</title>
      <dc:creator>Kasun de Silva</dc:creator>
      <pubDate>Thu, 27 Feb 2025 23:04:27 +0000</pubDate>
      <link>https://dev.to/aws-builders/streamline-your-development-workflow-with-codebuild-hosted-github-actions-runner-307k</link>
      <guid>https://dev.to/aws-builders/streamline-your-development-workflow-with-codebuild-hosted-github-actions-runner-307k</guid>
      <description>&lt;h3&gt;
  
  
  Serverless GitHub Actions? Yes! Learn how to use AWS Lambda/ CodeBuild for Github Actions workflow execution
&lt;/h3&gt;

&lt;p&gt;GitHub Actions provides a powerful CI/CD pipeline, but sometimes you need more control over the environment where your workflows run. AWS CodeBuild now supports Lambda-based self-hosted runners, allowing you to execute GitHub Actions workflows in a cost-efficient, on-demand AWS environment. This guide will walk you through setting up a self-hosted GitHub Actions runner using AWS CodeBuild's Lambda Runner environment.&lt;/p&gt;




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

&lt;p&gt;Before getting started, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An AWS account with permissions to create CodeBuild projects and Lambda functions&lt;/li&gt;
&lt;li&gt;A GitHub repository where you want to run the actions&lt;/li&gt;
&lt;li&gt;AWS CLI and GitHub CLI installed on your local machine&lt;/li&gt;
&lt;li&gt;An IAM role with necessary permissions for CodeBuild and Lambda execution&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1: Create an AWS CodeBuild Project&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;AWS CodeBuild will serve as our self-hosted GitHub Actions runner. Follow these steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1.1 Navigate to AWS CodeBuild&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Open the &lt;a href="https://console.aws.amazon.com/codebuild/home" rel="noopener noreferrer"&gt;AWS CodeBuild Console&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create build project&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1.2 Configure Project Settings&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project Name&lt;/strong&gt;: &lt;code&gt;github-actions-runner&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: &lt;code&gt;Self-hosted GitHub Actions runner using AWS CodeBuild Lambda Runner&lt;/code&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%2Fv0ckb9b44frm0dgy3tcf.png" alt="Image description" width="613" height="680"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt;: Select &lt;strong&gt;GitHub&lt;/strong&gt; and connect to your repository
&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%2Foskjnrt5kiljvhqi4of6.png" alt="Image description" width="617" height="675"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1.3 Select Environment&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Environment Image&lt;/strong&gt;: Choose &lt;strong&gt;Managed Image&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operating System&lt;/strong&gt;: Amazon Linux 2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime&lt;/strong&gt;: AWS Lambda&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute Type&lt;/strong&gt;: Lambda Execution Environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operating System System&lt;/strong&gt;: Amazon Linux&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime&lt;/strong&gt;: Python&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image &amp;amp; Version&lt;/strong&gt;: Select the latest&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%2Fkq9ada3mgore9d8jgmlk.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%2Fkq9ada3mgore9d8jgmlk.png" alt="Image description" width="620" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1.4 Configure IAM Role&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Choose &lt;strong&gt;New Service Role&lt;/strong&gt; or select an existing role with permissions to execute CodeBuild, interact with GitHub, and access AWS Lambda.&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%2F0qioil2gr0mes5dlhq5m.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%2F0qioil2gr0mes5dlhq5m.png" alt="Image description" width="608" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attach the following policies if needed:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:*:*:function:*"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"codebuild:StartBuild"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click &lt;strong&gt;Create Build Project&lt;/strong&gt; to finalize.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2: Setup the GitHub action workflow.&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to your GitHub repository&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;Actions&lt;/code&gt; tab and create a simple workflow.&lt;/li&gt;
&lt;li&gt;To point the lambda self-hosted runner in place, update your &lt;code&gt;.github/workflows/main.yml&lt;/code&gt; to use it:
Please note the &lt;code&gt;runs-on:&lt;/code&gt; section here that you need to at to the workflow file.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;codebuild-gha-runner-lambda-test-${{ github.run_id }}-${{ github.run_attempt }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a full example of the workflow file.&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 AWS Lambda Pipeline Test&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 "master" 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;master"&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;master"&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="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;codebuild-gha-runner-lambda-test-${{ github.run_id }}-${{ github.run_attempt }}&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;Push changes to your repository, and the self-hosted runner in AWS CodeBuild will pick up the job and the lambda function will run your job.&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%2Fwy0m7t3jxqsz9ujeo6ir.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%2Fwy0m7t3jxqsz9ujeo6ir.png" alt="Image description" width="800" height="372"&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%2Fvhm7xq52wazdkdcolcam.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%2Fvhm7xq52wazdkdcolcam.png" alt="Image description" width="800" height="138"&gt;&lt;/a&gt;&lt;/p&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzi9btivh673hmbs1n3n.png" alt="Image description" width="800" height="355"&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2.1 Limitations&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Lambda compute is designed for speed, optimizing startup times for builds. However, it does have some limitations and does not support the following use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reserved Capacity&lt;/li&gt;
&lt;li&gt;Caching Across Builds&lt;/li&gt;
&lt;li&gt;Restricting Runtime with Timeouts&lt;/li&gt;
&lt;li&gt;Tools Requiring Root-User Permissions&lt;/li&gt;
&lt;li&gt;Long-Running Builds (Lambda has a maximum timeout of 15 minutes)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Using AWS CodeBuild's Lambda Runner for GitHub Actions provides a scalable, cost-efficient alternative to traditional self-hosted runners. You only pay for execution time, and the Lambda-based environment ensures seamless scaling. Try this setup for your CI/CD workflows and enjoy the flexibility of AWS CodeBuild in your GitHub Actions pipelines!&lt;/p&gt;

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

</description>
      <category>githubactions</category>
      <category>lambda</category>
      <category>aws</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Speak the Command, Execute the Lambda</title>
      <dc:creator>Kasun de Silva</dc:creator>
      <pubDate>Thu, 19 Dec 2024 22:06:52 +0000</pubDate>
      <link>https://dev.to/aws-builders/triggering-aws-lambda-functions-with-voice-commands-using-amazon-alexa-35d8</link>
      <guid>https://dev.to/aws-builders/triggering-aws-lambda-functions-with-voice-commands-using-amazon-alexa-35d8</guid>
      <description>&lt;p&gt;In today's world of voice assistants and smart devices, integrating your AWS Lambda functions with Amazon Alexa opens up a world of possibilities. Imagine being able to trigger a Lambda function with a simple voice command, automating tasks like checking the weather, controlling smart devices, or querying your AWS resources. In this blog post, we’ll walk you through the steps to create a voice-activated Lambda function using Amazon Alexa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Setting Up Your AWS Lambda Function
&lt;/h2&gt;

&lt;p&gt;Before you can trigger a Lambda function with your voice, you need to create and configure the function.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1 Create the Lambda Function
&lt;/h3&gt;

&lt;p&gt;Log in to the AWS Management Console and navigate to the Lambda service.&lt;/p&gt;

&lt;p&gt;Create a new function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose "Author from scratch."&lt;/li&gt;
&lt;li&gt;Enter a function name (e.g., &lt;code&gt;WeatherCheckerLambda&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Choose a runtime (e.g., Python 3.9 or Node.js 18.x).&lt;/li&gt;
&lt;li&gt;Set up an execution role with basic Lambda permissions. If your function needs to access other AWS services, ensure the role has the appropriate permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Write your function code: Here’s a simple Python example that returns a weather update:&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;json&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="c1"&gt;# Sample response to be returned by Alexa
&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;version&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;1.0&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;response&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;outputSpeech&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;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;PlainText&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;text&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;The weather today is sunny with a high of 25 degrees.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;shouldEndSession&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.2 Configure the Lambda Function
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Set up environment variables and resource limits according to your needs.&lt;/li&gt;
&lt;li&gt;Test the function using the AWS Lambda console's built-in test feature. This ensures that your function executes as expected before integrating with Alexa.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Creating an Alexa Skill
&lt;/h2&gt;

&lt;p&gt;Now that your Lambda function is ready, you’ll create an Alexa skill to trigger it.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Set Up the Alexa Skill
&lt;/h3&gt;

&lt;p&gt;Log in to the &lt;a href="https://developer.amazon.com/alexa/console/ask" rel="noopener noreferrer"&gt;Alexa Developer Console&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create a new skill:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose “Create Skill” and select the “Custom” skill type.&lt;/li&gt;
&lt;li&gt;Set your skill name (e.g., &lt;code&gt;Weather Checker&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;other&lt;/code&gt; for the &lt;code&gt;experience&lt;/code&gt; type.&lt;/li&gt;
&lt;li&gt;Choose a template: Start from scratch or use the provided templates for a custom experience.&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%2Fhr0ifcy7y8c5bblv2emq.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%2Fhr0ifcy7y8c5bblv2emq.png" alt="Image description" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hit &lt;code&gt;Create Skill&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.2 Designing the Interaction Model
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Invocation Name:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set an invocation name for your skill (e.g., &lt;code&gt;weather checker&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;This is what users will say to start the interaction (e.g., “Alexa, ask weather checker...”).&lt;/li&gt;
&lt;li&gt;Hit &lt;code&gt;Build Skill&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Create Intents:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intents represent actions users want to perform. For example, create an intent called &lt;code&gt;GetWeatherIntent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Under the &lt;code&gt;GetWeatherIntent&lt;/code&gt;, add sample utterances like:

&lt;ul&gt;
&lt;li&gt;"What is the weather today?"&lt;/li&gt;
&lt;li&gt;"Tell me the weather."&lt;/li&gt;
&lt;li&gt;"Give me today's weather forecast."&lt;/li&gt;
&lt;/ul&gt;


&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%2Fp7xkd5zz1ygcvlk5bmwp.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%2Fp7xkd5zz1ygcvlk5bmwp.png" alt="Image description" width="800" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Slots:&lt;/strong&gt; If your skill needs more specific inputs, such as a city name, you can define slots. For this example, a simple weather checker might not need them.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 Configuring the Endpoint
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Link the Skill to Your Lambda Function:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the Alexa Developer Console, navigate to the Endpoint section.&lt;/li&gt;
&lt;li&gt;Select AWS Lambda ARN as the service endpoint type.&lt;/li&gt;
&lt;li&gt;Paste the ARN of the Lambda function you created earlier.&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%2Fo79wqg6p0jdeu3wwxhfu.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%2Fo79wqg6p0jdeu3wwxhfu.png" alt="Image description" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skill Permissions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure your Lambda function’s IAM role has the necessary permissions to execute and return data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Add Lambda Trigger&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%2Fb5mc5vpq1y105sjag2v0.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%2Fb5mc5vpq1y105sjag2v0.png" alt="Image description" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add “Alexa Skills kit” from the “Add triggers” sidebar in aws console. (Shown as in screenshot above)&lt;/p&gt;

&lt;p&gt;Next you have to click on the “Alexa Skills kit”, then this will show up&lt;/p&gt;

&lt;p&gt;Add your skill ID in the above field.&lt;/p&gt;

&lt;p&gt;Moreover, make sure you add this lambda function ARN in your skill’s endpoint. Just paste the ARN in your skill’s endpoint.&lt;/p&gt;

&lt;p&gt;Build and Save it.&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%2Fgou5vgqxd8gq9jnff9gm.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%2Fgou5vgqxd8gq9jnff9gm.png" alt="Image description" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Coding the Lambda Function to Handle Alexa Requests
&lt;/h2&gt;

&lt;p&gt;Your Lambda function needs to be able to handle requests from Alexa and respond appropriately.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Handle Alexa Requests in Your Lambda Function
&lt;/h3&gt;

&lt;p&gt;Here’s an expanded version of the Lambda function that parses the input and returns a relevant response:&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;json&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="c1"&gt;# Extracting intent name from Alexa's request
&lt;/span&gt;    &lt;span class="n"&gt;intent_name&lt;/span&gt; &lt;span class="o"&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;request&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;intent&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;if&lt;/span&gt; &lt;span class="n"&gt;intent_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GetWeatherIntent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Build response
&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;version&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;1.0&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;response&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;outputSpeech&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;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;PlainText&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;text&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;The weather today is sunny with a high of 25 degrees.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;shouldEndSession&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
            &lt;span class="p"&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;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;version&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;1.0&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;response&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;outputSpeech&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;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;PlainText&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;text&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;I am not sure how to help with that.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;shouldEndSession&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 Testing the Lambda Function
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use the AWS Lambda console to trigger the function manually with test events that simulate Alexa requests.&lt;/li&gt;
&lt;li&gt;Enable detailed logging in CloudWatch to troubleshoot any issues and ensure your function handles requests correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Testing and Deploying the Alexa Skill
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Testing with the Alexa Simulator
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Go to the Alexa Developer Console and open your skill.&lt;/li&gt;
&lt;li&gt;Use the Test tab: Here, you can simulate voice commands using the Alexa Simulator. Type or speak phrases like “Alexa, ask weather checker what the weather is today.”&lt;/li&gt;
&lt;li&gt;Review responses: Ensure Alexa returns the expected responses, and debug if necessary.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.2 Testing with a Real Device
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Enable your skill on an Alexa-enabled device, such as an Amazon Echo.&lt;/li&gt;
&lt;li&gt;Speak the invocation name and command: For example, say “Alexa, ask weather checker what the weather is today.”&lt;/li&gt;
&lt;li&gt;Refine based on feedback: Adjust your Lambda function and skill interaction model based on real-world testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5: Deploying and Monitoring Your Skill
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Deploying Your Skill
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Publish your skill: Once satisfied with the functionality, you can submit your skill for certification through the Alexa Developer Console.&lt;/li&gt;
&lt;li&gt;Private or Public: Choose whether to keep the skill private for personal use or make it available to the public.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.2 Monitoring and Logging
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use AWS CloudWatch: Monitor logs to track Lambda execution, identify errors, and optimize performance.&lt;/li&gt;
&lt;li&gt;Skill Analytics: In the Alexa Developer Console, you can also access analytics to see how users interact with your skill.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following these steps, you’ve successfully created a voice-activated Lambda function using Amazon Alexa. This setup opens the door to endless possibilities, from automating daily tasks to creating rich, voice-controlled applications. Whether you’re developing a personal project or a commercial application, integrating AWS Lambda with Alexa is a powerful way to bring voice capabilities to your cloud services.&lt;/p&gt;

&lt;p&gt;Happy coding, and enjoy the convenience of voice-triggered automation!&lt;/p&gt;

</description>
      <category>lambda</category>
      <category>aws</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Mastering AWS Step Functions Error Handling</title>
      <dc:creator>Kasun de Silva</dc:creator>
      <pubDate>Thu, 01 Aug 2024 23:18:01 +0000</pubDate>
      <link>https://dev.to/aws-builders/mastering-aws-step-functions-error-handling-j39</link>
      <guid>https://dev.to/aws-builders/mastering-aws-step-functions-error-handling-j39</guid>
      <description>&lt;p&gt;AWS Step Functions is a powerful orchestration service that enables developers to build and coordinate workflows using a series of steps, such as AWS Lambda functions, ECS tasks, or other AWS services. One of the critical aspects of building robust workflows is handling errors effectively. In this blog post, we'll dive into the different error handling scenarios in AWS Step Functions and provide practical examples to illustrate how to manage them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Error Handling is Important
&lt;/h3&gt;

&lt;p&gt;Error handling ensures your workflows can gracefully handle failures and continue processing without manual intervention. This not only improves the reliability of your applications but also enhances user experience by minimizing downtime and reducing the likelihood of data corruption.&lt;/p&gt;

&lt;h3&gt;
  
  
  Types of Errors in AWS Step Functions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;States.All Errors&lt;/strong&gt;: Catch-all for any error not explicitly caught by other patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;States.Timeout&lt;/strong&gt;: Triggered when a state exceeds its allowed execution time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;States.TaskFailed&lt;/strong&gt;: Raised when a task state fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;States.Permissions&lt;/strong&gt;: Occurs due to IAM permission issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;States.ResultPathMatchFailure&lt;/strong&gt;: When the result path doesn't match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;States.BranchFailed&lt;/strong&gt;: Raised if a parallel state fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;States.NoChoiceMatched&lt;/strong&gt;: No match found for a Choice state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;States.ParameterPathFailure&lt;/strong&gt;: When a parameter path evaluation fails.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Error Handling Strategies
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retry&lt;/strong&gt;: Automatically retry a failed state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Catch&lt;/strong&gt;: Capture errors and redirect execution to a recovery path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout&lt;/strong&gt;: Specify a maximum time a state should run.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example Workflow
&lt;/h3&gt;

&lt;p&gt;Let's create a Step Functions workflow with a few states to illustrate error handling. Our example will include a Lambda function that might fail, and we'll handle errors using retry and catch mechanisms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State Machine Graph&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.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%2Fc1pgs1a7xn5pvdvcdxva.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fc1pgs1a7xn5pvdvcdxva.png" alt="Step Function"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step Function Definition&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A simple state machine to demonstrate error handling"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"StartAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invoke Lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"States"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Invoke Lambda"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:REGION:ACCOUNT_ID:function:YOUR_LAMBDA_FUNCTION"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Retry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"States.ALL"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"IntervalSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"MaxAttempts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"BackoffRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Catch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"States.ALL"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Handle Error"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"End"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Handle Error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LambdaFunctionFailed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Cause"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Lambda function encountered an error."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Error Handling Scenarios
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1. Retrying Failed States
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;Retry&lt;/code&gt; field allows you to retry a failed state. In the example above, the state will retry up to 3 times with exponential backoff if an error occurs.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"Retry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"States.ALL"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"IntervalSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"MaxAttempts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"BackoffRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;
&lt;h4&gt;
  
  
  2. Catching Errors
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;Catch&lt;/code&gt; field enables you to capture errors and redirect the workflow to a different state, like an error handler or a fallback mechanism.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"Catch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"States.ALL"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Handle Error"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;
&lt;h4&gt;
  
  
  3. Handling Timeouts
&lt;/h4&gt;

&lt;p&gt;You can specify timeouts for states to prevent them from running indefinitely.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:REGION:ACCOUNT_ID:function:YOUR_LAMBDA_FUNCTION"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"TimeoutSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;




&lt;h3&gt;
  
  
  Advanced Error Handling
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Conditional Error Handling with Choice State
&lt;/h4&gt;

&lt;p&gt;You can use the Choice state to direct the workflow based on different error types.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"Catch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"States.Timeout"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TimeoutHandler"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"States.TaskFailed"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TaskFailedHandler"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Benefits of Conditional Error Handling&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Granular Control&lt;/strong&gt;: Allows you to define different handling strategies for different error types, improving the robustness of your workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Debugging&lt;/strong&gt;: By routing specific errors to distinct states, you can more easily identify and address issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customised Recovery&lt;/strong&gt;: Enables tailored recovery actions or notifications based on the nature of the error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;State Machine Graph&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.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%2Fe7bin9sf8hi4pkfae6qe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fe7bin9sf8hi4pkfae6qe.png" alt="Timeout erros"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step Function Definition&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A simple state machine to demonstrate error handling including timeout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"StartAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invoke Lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"States"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Invoke Lambda"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:REGION:ACCOUNT_ID:function:YOUR_LAMBDA_FUNCTION"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"TimeoutSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Timeout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;seconds&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Retry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"States.ALL"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"IntervalSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"MaxAttempts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"BackoffRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Catch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"States.Timeout"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Handle Timeout"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"States.ALL"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Handle Error"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"End"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Handle Timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LambdaTimeoutError"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Cause"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Lambda function timed out."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Handle Error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LambdaFunctionFailed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Cause"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Lambda function encountered an error."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;
&lt;h4&gt;
  
  
  2. Parallel State Error Handling
&lt;/h4&gt;

&lt;p&gt;For workflows with parallel states, each branch can have its own error handling strategy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Parallel Tasks State:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Parallel state starts two branches: "Invoke Lambda A" and "Invoke Lambda B".&lt;/li&gt;
&lt;li&gt;Each branch handles retries, timeouts, and failures independently.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Error Handling in Each Branch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retry: Retries the task up to 3 times with exponential backoff if it fails.&lt;/li&gt;
&lt;li&gt;Timeout: If a task times out, it transitions to a specific error handler.&lt;/li&gt;
&lt;li&gt;Catch: Captures any other errors and transitions to an error handler.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Error Handling for Parallel State:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Catch block in the Parallel state catches errors from any branch and transitions to the "Handle Parallel Failure" state if any branch fails.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;State Machine Graph&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.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%2Fnb86xs07ld8mk6ubs4ku.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fnb86xs07ld8mk6ubs4ku.png" alt="Parallel State Error Handling"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step Function Definition&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A state machine with parallel tasks and error handling"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"StartAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Parallel Tasks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"States"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Parallel Tasks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Parallel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Branches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"StartAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invoke Lambda A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"States"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Invoke Lambda A"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:REGION:ACCOUNT_ID:function:LambdaFunctionA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"TimeoutSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Retry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"States.ALL"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"IntervalSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"MaxAttempts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"BackoffRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Catch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"States.Timeout"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Handle Timeout A"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"States.ALL"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Handle Error A"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"End"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Handle Timeout A"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LambdaTimeoutErrorA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Cause"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lambda Function A timed out."&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Handle Error A"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LambdaFunctionFailedA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Cause"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lambda Function A failed."&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"StartAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invoke Lambda B"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"States"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Invoke Lambda B"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:REGION:ACCOUNT_ID:function:LambdaFunctionB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"TimeoutSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Retry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"States.ALL"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"IntervalSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"MaxAttempts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"BackoffRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Catch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"States.Timeout"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Handle Timeout B"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"States.ALL"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Handle Error B"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"End"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Handle Timeout B"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LambdaTimeoutErrorB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Cause"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lambda Function B timed out."&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Handle Error B"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LambdaFunctionFailedB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"Cause"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lambda Function B failed."&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Catch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"ErrorEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"States.ALL"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Handle Parallel Failure"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"End"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Handle Parallel Failure"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ParallelStateFailed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Cause"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"One or more parallel tasks failed."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;Effective error handling in AWS Step Functions is crucial for building resilient workflows. By leveraging retry, catch, and timeout strategies, you can ensure your workflows handle failures gracefully and continue processing without manual intervention. With these techniques, you can build robust and reliable applications that can withstand various failure scenarios.&lt;/p&gt;

&lt;p&gt;Do you have any questions or additional error handling scenarios you'd like to explore? Let me know in the comments below! Happy coding in AWS!&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>stepfunctions</category>
      <category>lambda</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
